aws-runtime-bridge 1.0.2 → 1.0.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/dist/config.d.ts CHANGED
@@ -67,7 +67,7 @@ export declare const AUTO_REGISTER_PROJECT_NAME: string;
67
67
  export declare const AUTO_REGISTER_ROLE_NAME: string;
68
68
  /** 工作空间路径 */
69
69
  export declare const AUTO_REGISTER_WORKSPACE_PATH: string;
70
- /** 虚拟 IPEasyTier */
70
+ /** 注册 IP(优先),兼容 EasyTier 虚拟 IP */
71
71
  export declare const AUTO_REGISTER_VIRTUAL_IP: string;
72
72
  /** 注册重试次数 */
73
73
  export declare const AUTO_REGISTER_MAX_RETRIES: number;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,WAAW;AACX,eAAO,MAAM,IAAI,EAAE,MAElB,CAAC;AAEF,gBAAgB;AAChB,eAAO,MAAM,gBAAgB,EAAE,MACwC,CAAC;AAExE,oBAAoB;AACpB,eAAO,MAAM,8BAA8B,gCAAgC,CAAC;AAE5E,cAAc;AACd,eAAO,MAAM,OAAO,EAAE,MAA8C,CAAC;AAErE,kBAAkB;AAClB,eAAO,MAAM,YAAY,EAAE,MAC+C,CAAC;AAE3E,2BAA2B;AAC3B,eAAO,MAAM,wBAAwB,EAAE,OACiB,CAAC;AAEzD,oCAAoC;AACpC,eAAO,MAAM,oBAAoB,EAAE,OACiB,CAAC;AAErD,8BAA8B;AAC9B,eAAO,MAAM,kBAAkB,EAAE,MAAM,EAMC,CAAC;AAEzC,uBAAuB;AACvB,wBAAgB,uBAAuB,IAAI,IAAI,CAc9C;AAED,eAAe;AACf,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,8CAA8C;AAC9C,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,2BAA2B;AAC3B,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKlE;AAED,0BAA0B;AAC1B,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED,yBAAyB;AACzB,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED,sBAAsB;AACtB,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED,4BAA4B;AAC5B,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED;;GAEG;AAEH,yBAAyB;AACzB,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,EACrB,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1C,eAAe;AACf,eAAO,MAAM,mBAAmB,SACiB,CAAC;AAElD,iCAAiC;AACjC,eAAO,MAAM,2BAA2B,QAEvC,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AAEF,qCAAqC;AACrC,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AAEF;;GAEG;AAEH,eAAe;AACf,eAAO,MAAM,qBAAqB,SAA2C,CAAC;AAE9E,YAAY;AACZ,eAAO,MAAM,uBAAuB,QAAkC,CAAC;AAEvE,iBAAiB;AACjB,eAAO,MAAM,sBAAsB,QAAiC,CAAC;AAErE,oBAAoB;AACpB,eAAO,MAAM,2BAA2B,QAAsC,CAAC;AAE/E,WAAW;AACX,eAAO,MAAM,0BAA0B,QAAqC,CAAC;AAE7E,WAAW;AACX,eAAO,MAAM,uBAAuB,QAAkC,CAAC;AAEvE,aAAa;AACb,eAAO,MAAM,4BAA4B,QACH,CAAC;AAEvC,sBAAsB;AACtB,eAAO,MAAM,wBAAwB,QAAmC,CAAC;AAEzE,aAAa;AACb,eAAO,MAAM,yBAAyB,QAErC,CAAC;AAEF,iBAAiB;AACjB,eAAO,MAAM,4BAA4B,QAExC,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,WAAW;AACX,eAAO,MAAM,IAAI,EAAE,MAElB,CAAC;AAEF,gBAAgB;AAChB,eAAO,MAAM,gBAAgB,EAAE,MACwC,CAAC;AAExE,oBAAoB;AACpB,eAAO,MAAM,8BAA8B,gCAAgC,CAAC;AAE5E,cAAc;AACd,eAAO,MAAM,OAAO,EAAE,MAA8C,CAAC;AAErE,kBAAkB;AAClB,eAAO,MAAM,YAAY,EAAE,MAC+C,CAAC;AAE3E,2BAA2B;AAC3B,eAAO,MAAM,wBAAwB,EAAE,OACiB,CAAC;AAEzD,oCAAoC;AACpC,eAAO,MAAM,oBAAoB,EAAE,OACiB,CAAC;AAErD,8BAA8B;AAC9B,eAAO,MAAM,kBAAkB,EAAE,MAAM,EAMC,CAAC;AAEzC,uBAAuB;AACvB,wBAAgB,uBAAuB,IAAI,IAAI,CAc9C;AAED,eAAe;AACf,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,8CAA8C;AAC9C,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,2BAA2B;AAC3B,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKlE;AAED,0BAA0B;AAC1B,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED,yBAAyB;AACzB,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED,sBAAsB;AACtB,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED,4BAA4B;AAC5B,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED;;GAEG;AAEH,yBAAyB;AACzB,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,EACrB,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1C,eAAe;AACf,eAAO,MAAM,mBAAmB,SACiB,CAAC;AAElD,iCAAiC;AACjC,eAAO,MAAM,2BAA2B,QAEvC,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AAEF,qCAAqC;AACrC,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AAEF;;GAEG;AAEH,eAAe;AACf,eAAO,MAAM,qBAAqB,SAA2C,CAAC;AAE9E,YAAY;AACZ,eAAO,MAAM,uBAAuB,QAAkC,CAAC;AAEvE,iBAAiB;AACjB,eAAO,MAAM,sBAAsB,QAAiC,CAAC;AAErE,oBAAoB;AACpB,eAAO,MAAM,2BAA2B,QAAsC,CAAC;AAE/E,WAAW;AACX,eAAO,MAAM,0BAA0B,QAAqC,CAAC;AAE7E,WAAW;AACX,eAAO,MAAM,uBAAuB,QAAkC,CAAC;AAEvE,aAAa;AACb,eAAO,MAAM,4BAA4B,QACH,CAAC;AAEvC,kCAAkC;AAClC,eAAO,MAAM,wBAAwB,QAC4B,CAAC;AAElE,aAAa;AACb,eAAO,MAAM,yBAAyB,QAErC,CAAC;AAEF,iBAAiB;AACjB,eAAO,MAAM,4BAA4B,QAExC,CAAC"}
package/dist/config.js CHANGED
@@ -104,8 +104,8 @@ export const AUTO_REGISTER_PROJECT_NAME = process.env.AWS_PROJECT_NAME || "";
104
104
  export const AUTO_REGISTER_ROLE_NAME = process.env.AWS_ROLE_NAME || "";
105
105
  /** 工作空间路径 */
106
106
  export const AUTO_REGISTER_WORKSPACE_PATH = process.env.AWS_WORKSPACE_PATH || "";
107
- /** 虚拟 IPEasyTier */
108
- export const AUTO_REGISTER_VIRTUAL_IP = process.env.AWS_VIRTUAL_IP || "";
107
+ /** 注册 IP(优先),兼容 EasyTier 虚拟 IP */
108
+ export const AUTO_REGISTER_VIRTUAL_IP = process.env.AWS_REGISTER_IP || process.env.AWS_VIRTUAL_IP || "";
109
109
  /** 注册重试次数 */
110
110
  export const AUTO_REGISTER_MAX_RETRIES = Number(process.env.AWS_REGISTER_MAX_RETRIES || 5);
111
111
  /** 注册重试间隔(毫秒) */
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"AAeA,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAE5G;AAED,eAAO,MAAM,cAAc,4CAAW,CAAC"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"AAwBA,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/D,IAAI,CAEN;AAED,eAAO,MAAM,cAAc,4CAAW,CAAC"}
@@ -1,12 +1,12 @@
1
- import { Router } from 'express';
2
- import axios from 'axios';
3
- import { validateToken } from '../middleware/auth.js';
4
- import { schedulerBaseUrl, runtimeToken } from '../config.js';
5
- import { loadInstanceState, saveInstanceState } from '../services/instance-state.js';
6
- import { initInstance } from '../services/instance-init-service.js';
7
- import { detectToolStatuses, ensureToolsInstalled, SUPPORTED_INSTALLABLE_TOOLS } from '../services/tool-installer.js';
8
- import { createLogger } from '../utils/logger.js';
9
- const log = createLogger('instance');
1
+ import { Router } from "express";
2
+ import axios from "axios";
3
+ import { validateToken } from "../middleware/auth.js";
4
+ import { schedulerBaseUrl, runtimeToken } from "../config.js";
5
+ import { loadInstanceState, saveInstanceState, } from "../services/instance-state.js";
6
+ import { initInstance } from "../services/instance-init-service.js";
7
+ import { detectToolStatuses, ensureToolsInstalled, SUPPORTED_INSTALLABLE_TOOLS, } from "../services/tool-installer.js";
8
+ import { createLogger } from "../utils/logger.js";
9
+ const log = createLogger("instance");
10
10
  // ★★★ 导出 gracefulShutdown 的触发函数
11
11
  // 用于 aws-mcp-server 通知 bridge 保留会话状态关闭
12
12
  let gracefulShutdownFn = null;
@@ -14,12 +14,18 @@ export function setGracefulShutdownFn(fn) {
14
14
  gracefulShutdownFn = fn;
15
15
  }
16
16
  export const instanceRouter = Router();
17
- instanceRouter.get('/ping', validateToken, async (_req, res) => {
17
+ instanceRouter.get("/healthz", (_req, res) => {
18
+ res.json({
19
+ ok: true,
20
+ runtimeBridge: "healthy",
21
+ });
22
+ });
23
+ instanceRouter.get("/ping", validateToken, async (_req, res) => {
18
24
  try {
19
- const schedulerResponse = await axios.get(`${schedulerBaseUrl}/api/runtime/ping`, { headers: { 'X-Runtime-Token': runtimeToken } });
25
+ const schedulerResponse = await axios.get(`${schedulerBaseUrl}/api/runtime/ping`, { headers: { "X-Runtime-Token": runtimeToken } });
20
26
  res.json({
21
27
  ok: true,
22
- runtimeBridge: 'healthy',
28
+ runtimeBridge: "healthy",
23
29
  schedulerReachable: schedulerResponse.status >= 200 && schedulerResponse.status < 300,
24
30
  schedulerBaseUrl,
25
31
  });
@@ -28,15 +34,15 @@ instanceRouter.get('/ping', validateToken, async (_req, res) => {
28
34
  const err = error;
29
35
  res.status(502).json({
30
36
  ok: false,
31
- error: err?.message || 'scheduler ping failed',
37
+ error: err?.message || "scheduler ping failed",
32
38
  schedulerBaseUrl,
33
39
  });
34
40
  }
35
41
  });
36
- instanceRouter.post('/init-instance', validateToken, async (req, res) => {
42
+ instanceRouter.post("/init-instance", validateToken, async (req, res) => {
37
43
  const { agentId, workspacePath, skillEnabled, mcpEnabled, ccSwitchEnabledTools, skillPackage, skillPackages, mcpServers, } = req.body || {};
38
44
  if (!agentId || !workspacePath) {
39
- res.status(400).json({ error: 'agentId and workspacePath are required' });
45
+ res.status(400).json({ error: "agentId and workspacePath are required" });
40
46
  return;
41
47
  }
42
48
  try {
@@ -53,39 +59,51 @@ instanceRouter.post('/init-instance', validateToken, async (req, res) => {
53
59
  catch (error) {
54
60
  const err = error;
55
61
  res.status(400).json({
56
- error: err?.message || 'initialize instance failed',
62
+ error: err?.message || "initialize instance failed",
57
63
  });
58
64
  }
59
65
  });
60
- instanceRouter.post('/cc-switch/state', validateToken, async (req, res) => {
66
+ instanceRouter.post("/cc-switch/state", validateToken, async (req, res) => {
61
67
  const { agentId } = req.body || {};
62
68
  if (!agentId) {
63
- res.status(400).json({ error: 'agentId is required' });
69
+ res.status(400).json({ error: "agentId is required" });
64
70
  return;
65
71
  }
66
72
  try {
67
73
  const state = await loadInstanceState(agentId);
68
74
  const toolStatus = await detectToolStatuses(state.enabledTools || []);
69
- res.json({ ok: true, agentId: String(agentId), state: { ...state, toolStatus } });
75
+ res.json({
76
+ ok: true,
77
+ agentId: String(agentId),
78
+ state: { ...state, toolStatus },
79
+ });
70
80
  }
71
81
  catch (error) {
72
82
  const err = error;
73
- res.status(400).json({ error: err?.message || 'load state failed' });
83
+ res.status(400).json({ error: err?.message || "load state failed" });
74
84
  }
75
85
  });
76
- instanceRouter.post('/cc-switch/install-tools', validateToken, async (req, res) => {
86
+ instanceRouter.post("/cc-switch/install-tools", validateToken, async (req, res) => {
77
87
  const { agentId, tools } = req.body || {};
78
88
  if (!agentId) {
79
- res.status(400).json({ error: 'agentId is required' });
89
+ res.status(400).json({ error: "agentId is required" });
80
90
  return;
81
91
  }
82
92
  const requestedTools = Array.isArray(tools)
83
- ? tools.map((tool) => String(tool || '').trim().toLowerCase()).filter(Boolean)
93
+ ? tools
94
+ .map((tool) => String(tool || "")
95
+ .trim()
96
+ .toLowerCase())
97
+ .filter(Boolean)
84
98
  : [];
85
99
  const supportedInstallableTools = new Set(SUPPORTED_INSTALLABLE_TOOLS);
86
100
  const installableTools = requestedTools.filter((tool) => supportedInstallableTools.has(tool));
87
101
  if (installableTools.length === 0) {
88
- res.status(400).json({ error: `tools must include ${SUPPORTED_INSTALLABLE_TOOLS.join(', ')}` });
102
+ res
103
+ .status(400)
104
+ .json({
105
+ error: `tools must include ${SUPPORTED_INSTALLABLE_TOOLS.join(", ")}`,
106
+ });
89
107
  return;
90
108
  }
91
109
  try {
@@ -111,7 +129,7 @@ instanceRouter.post('/cc-switch/install-tools', validateToken, async (req, res)
111
129
  }
112
130
  catch (error) {
113
131
  const err = error;
114
- res.status(400).json({ error: err?.message || 'install tools failed' });
132
+ res.status(400).json({ error: err?.message || "install tools failed" });
115
133
  }
116
134
  });
117
135
  /**
@@ -126,22 +144,22 @@ instanceRouter.post('/cc-switch/install-tools', validateToken, async (req, res)
126
144
  * - preserveSessions: true = 保留会话(aws-mcp-server 重启)
127
145
  * false = 清理会话(bridge 自己关闭)
128
146
  */
129
- instanceRouter.post('/prepare-restart', validateToken, async (req, res) => {
147
+ instanceRouter.post("/prepare-restart", validateToken, async (req, res) => {
130
148
  const { preserveSessions = true } = req.body || {};
131
149
  try {
132
150
  log.info(`[prepare-restart] 收到准备重启通知,preserveSessions=${preserveSessions}`);
133
151
  // 记录重启模式到文件,供下次启动时判断
134
- const fs = await import('fs/promises');
135
- const path = await import('path');
136
- const os = await import('os');
137
- const restartFlagFile = path.join(os.tmpdir(), 'agentswork-runtime-bridge', '.preserve-sessions');
152
+ const fs = await import("fs/promises");
153
+ const path = await import("path");
154
+ const os = await import("os");
155
+ const restartFlagFile = path.join(os.tmpdir(), "agentswork-runtime-bridge", ".preserve-sessions");
138
156
  if (preserveSessions) {
139
157
  // 创建标记文件,表示 aws-mcp-server 重启,需要保留会话
140
158
  await fs.mkdir(path.dirname(restartFlagFile), { recursive: true });
141
159
  await fs.writeFile(restartFlagFile, JSON.stringify({
142
160
  preserveSessions: true,
143
161
  timestamp: new Date().toISOString(),
144
- }), 'utf-8');
162
+ }), "utf-8");
145
163
  log.info(`[prepare-restart] 已创建保留会话标记文件: ${restartFlagFile}`);
146
164
  }
147
165
  else {
@@ -158,13 +176,13 @@ instanceRouter.post('/prepare-restart', validateToken, async (req, res) => {
158
176
  ok: true,
159
177
  preserveSessions,
160
178
  message: preserveSessions
161
- ? 'Bridge will preserve sessions for aws-mcp-server restart'
162
- : 'Bridge will clean up sessions on shutdown',
179
+ ? "Bridge will preserve sessions for aws-mcp-server restart"
180
+ : "Bridge will clean up sessions on shutdown",
163
181
  });
164
182
  }
165
183
  catch (error) {
166
184
  const err = error;
167
- log.error('[prepare-restart] Error:', err);
185
+ log.error("[prepare-restart] Error:", err);
168
186
  res.status(500).json({ error: err.message });
169
187
  }
170
188
  });
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-binding.d.ts","sourceRoot":"","sources":["../../src/routes/runtime-binding.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,oBAAoB,4CAAW,CAAC;AAkK7C,wBAAgB,6BAA6B,IAAI,IAAI,CAapD"}
1
+ {"version":3,"file":"runtime-binding.d.ts","sourceRoot":"","sources":["../../src/routes/runtime-binding.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,oBAAoB,4CAAW,CAAC;AAkL7C,wBAAgB,6BAA6B,IAAI,IAAI,CAapD"}
@@ -24,7 +24,7 @@ runtimeBindingRouter.get("/binding/status", (_req, res) => {
24
24
  });
25
25
  });
26
26
  runtimeBindingRouter.post("/binding/pair", (req, res) => {
27
- const { accessToken, instanceId, schedulerBaseUrl, pairingCode } = req.body || {};
27
+ const { accessToken, instanceId, userId, schedulerBaseUrl, pairingCode } = req.body || {};
28
28
  const existingBinding = hasRuntimeBinding();
29
29
  const currentRuntimeToken = extractRuntimeToken(req);
30
30
  if (existingBinding) {
@@ -48,6 +48,7 @@ runtimeBindingRouter.post("/binding/pair", (req, res) => {
48
48
  saveRuntimeBinding({
49
49
  accessToken: String(accessToken || ""),
50
50
  instanceId: instanceId ? String(instanceId) : undefined,
51
+ userId: userId ? String(userId) : undefined,
51
52
  schedulerBaseUrl: schedulerBaseUrl ? String(schedulerBaseUrl) : undefined,
52
53
  });
53
54
  log.info(`[runtime-bridge] paired with instanceId=${instanceId || "unknown"}`);
@@ -72,6 +73,16 @@ runtimeBindingRouter.post("/binding/request-token", async (req, res) => {
72
73
  res.status(403).json({ ok: false, error: "loopback request required" });
73
74
  return;
74
75
  }
76
+ const { userId, serverUrl, schedulerBaseUrl } = req.body || {};
77
+ const scopedToken = getRuntimeAccessToken(userId, serverUrl || schedulerBaseUrl);
78
+ if (scopedToken) {
79
+ res.json({
80
+ ok: true,
81
+ runtimeAccessToken: scopedToken,
82
+ updated: false,
83
+ });
84
+ return;
85
+ }
75
86
  const result = await requestRuntimeAccessTokenRefresh();
76
87
  if (!result.success || !result.runtimeAccessToken) {
77
88
  res.status(400).json({
@@ -94,7 +105,7 @@ runtimeBindingRouter.post("/binding/issue", (req, res) => {
94
105
  res.status(401).json({ error: "unauthorized" });
95
106
  return;
96
107
  }
97
- const { accessToken, instanceId, schedulerBaseUrl } = req.body || {};
108
+ const { accessToken, instanceId, userId, schedulerBaseUrl } = req.body || {};
98
109
  const nextToken = String(accessToken || "").trim();
99
110
  if (!nextToken) {
100
111
  res.status(400).json({ error: "accessToken is required" });
@@ -107,6 +118,7 @@ runtimeBindingRouter.post("/binding/issue", (req, res) => {
107
118
  saveRuntimeBinding({
108
119
  accessToken: nextToken,
109
120
  instanceId: instanceId ? String(instanceId) : undefined,
121
+ userId: userId ? String(userId) : undefined,
110
122
  schedulerBaseUrl: schedulerBaseUrl
111
123
  ? String(schedulerBaseUrl)
112
124
  : undefined,
@@ -25,7 +25,9 @@ interface AutoRegisterConfig {
25
25
  roleName?: string;
26
26
  /** 工作空间路径 */
27
27
  workspacePath?: string;
28
- /** 虚拟 IP(EasyTier) */
28
+ /** 注册 IP(优先用于生成 runtimeBridgeBaseUrl) */
29
+ registerIp?: string;
30
+ /** 虚拟 IP(EasyTier,兼容旧配置) */
29
31
  virtualIp?: string;
30
32
  /** 注册重试次数 */
31
33
  maxRetries?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"auto-register.d.ts","sourceRoot":"","sources":["../../src/services/auto-register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4CH;;GAEG;AACH,UAAU,kBAAkB;IAC1B,eAAe;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAiKD;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,kBAAkB,CAuC/C;AAuLD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,YAAY,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GACzC,OAAO,CAAC,OAAO,CAAC,CAmHlB;AAED;;GAEG;AACH,wBAAsB,gCAAgC,IAAI,OAAO,CAAC;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAoED;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAuBnD;AAED;;GAEG;AACH,wBAAgB,oBAAoB;gBAxmBtB,OAAO;iBACN,MAAM;kBACL,IAAI;YACV,MAAM;EAumBf;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CA0ED"}
1
+ {"version":3,"file":"auto-register.d.ts","sourceRoot":"","sources":["../../src/services/auto-register.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6CH;;GAEG;AACH,UAAU,kBAAkB;IAC1B,eAAe;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAsKD;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,kBAAkB,CAwC/C;AAuLD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,YAAY,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GACzC,OAAO,CAAC,OAAO,CAAC,CAoHlB;AAED;;GAEG;AACH,wBAAsB,gCAAgC,IAAI,OAAO,CAAC;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAoED;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAuBnD;AAED;;GAEG;AACH,wBAAgB,oBAAoB;gBA/mBtB,OAAO;iBACN,MAAM;kBACL,IAAI;YACV,MAAM;EA8mBf;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CA0ED"}
@@ -71,7 +71,8 @@ function ensureConfigFile() {
71
71
  * "projectName": "default", // 可选
72
72
  * "roleName": "Bridge", // 可选
73
73
  * "workspacePath": "/path/to/workspace", // 可选
74
- * "virtualIp": "10.144.144.1", // 可选
74
+ * "registerIp": "192.168.1.25", // 可选,优先作为注册 IP
75
+ * "virtualIp": "10.144.144.1", // 可选,兼容 EasyTier 虚拟 IP
75
76
  * "maxRetries": 5, // 可选
76
77
  * "retryInterval": 5000 // 可选
77
78
  * }
@@ -95,6 +96,7 @@ function loadConfigFromFile() {
95
96
  projectName: config.projectName,
96
97
  roleName: config.roleName,
97
98
  workspacePath: config.workspacePath,
99
+ registerIp: config.registerIp,
98
100
  virtualIp: config.virtualIp,
99
101
  maxRetries: config.maxRetries,
100
102
  retryInterval: config.retryInterval,
@@ -132,6 +134,9 @@ function loadConfigFromEnv() {
132
134
  if (process.env.AWS_WORKSPACE_PATH) {
133
135
  envConfig.workspacePath = process.env.AWS_WORKSPACE_PATH;
134
136
  }
137
+ if (process.env.AWS_REGISTER_IP) {
138
+ envConfig.registerIp = process.env.AWS_REGISTER_IP;
139
+ }
135
140
  if (process.env.AWS_VIRTUAL_IP) {
136
141
  envConfig.virtualIp = process.env.AWS_VIRTUAL_IP;
137
142
  }
@@ -181,6 +186,7 @@ export function loadConfig() {
181
186
  projectName: merged.projectName,
182
187
  roleName: merged.roleName,
183
188
  workspacePath: merged.workspacePath,
189
+ registerIp: merged.registerIp,
184
190
  virtualIp: merged.virtualIp,
185
191
  maxRetries: merged.maxRetries,
186
192
  retryInterval: merged.retryInterval,
@@ -298,8 +304,8 @@ async function doRegister(config) {
298
304
  userKey: config.userKey,
299
305
  instanceName,
300
306
  hostname: os.hostname(),
301
- virtualIp: config.virtualIp,
302
- runtimeBridgeBaseUrl: `http://${config.virtualIp || localIp}:${port}`,
307
+ virtualIp: config.registerIp || config.virtualIp,
308
+ runtimeBridgeBaseUrl: `http://${config.registerIp || config.virtualIp || localIp}:${port}`,
303
309
  workspacePath: config.workspacePath,
304
310
  projectName: config.projectName,
305
311
  roleName: config.roleName,
@@ -379,6 +385,7 @@ export async function autoRegister(customConfig) {
379
385
  saveRuntimeBinding({
380
386
  accessToken: response.runtimeAccessToken,
381
387
  instanceId: response.instanceId,
388
+ userId: response.userId,
382
389
  schedulerBaseUrl: response.serverUrl || schedulerBaseUrl,
383
390
  });
384
391
  logger.info("[AutoRegister] 已保存调度中心下发的 runtime access token");
@@ -441,7 +448,7 @@ export async function requestRuntimeAccessTokenRefresh() {
441
448
  const state = getRuntimeBindingPublicState();
442
449
  const localIp = getLocalIpAddress();
443
450
  const runtimeBridgePort = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
444
- const runtimeBridgeBaseUrl = `http://${config.virtualIp || localIp}:${runtimeBridgePort}`;
451
+ const runtimeBridgeBaseUrl = `http://${config.registerIp || config.virtualIp || localIp}:${runtimeBridgePort}`;
445
452
  try {
446
453
  const response = await axios.post(`${schedulerBaseUrl}/api/instances/runtime-tokens/refresh`, {
447
454
  tenantId: config.tenantId,
@@ -465,13 +472,12 @@ export async function requestRuntimeAccessTokenRefresh() {
465
472
  }
466
473
  const previousToken = getRuntimeAccessToken();
467
474
  const updated = previousToken !== response.data.runtimeAccessToken;
468
- if (updated) {
469
- saveRuntimeBinding({
470
- accessToken: response.data.runtimeAccessToken,
471
- instanceId: state.instanceId,
472
- schedulerBaseUrl,
473
- });
474
- }
475
+ saveRuntimeBinding({
476
+ accessToken: response.data.runtimeAccessToken,
477
+ instanceId: state.instanceId,
478
+ userId: response.data.userId || config.userKey,
479
+ schedulerBaseUrl,
480
+ });
475
481
  return {
476
482
  success: true,
477
483
  runtimeAccessToken: response.data.runtimeAccessToken,
@@ -552,7 +558,7 @@ export async function bridgeRestartCleanup() {
552
558
  if (config.enabled) {
553
559
  const localIp = getLocalIpAddress();
554
560
  const port = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
555
- requestBody.runtimeBridgeBaseUrl = `http://${config.virtualIp || localIp}:${port}`;
561
+ requestBody.runtimeBridgeBaseUrl = `http://${config.registerIp || config.virtualIp || localIp}:${port}`;
556
562
  }
557
563
  if (!requestBody.instanceId && !requestBody.runtimeBridgeBaseUrl) {
558
564
  logger.info("[AutoRegister] Bridge 重启清理:没有 instanceId 或 runtimeBridgeBaseUrl,跳过清理");
@@ -1,6 +1,7 @@
1
1
  export type RuntimeBindingState = {
2
2
  status: "unpaired" | "paired";
3
3
  instanceId?: string;
4
+ userId?: string;
4
5
  schedulerBaseUrl?: string;
5
6
  /**
6
7
  * Plain runtime access token issued by the scheduler.
@@ -15,17 +16,26 @@ export type RuntimeBindingState = {
15
16
  };
16
17
  export declare function getRuntimePairingCode(): string;
17
18
  export declare function getRuntimeBindingFilePath(): string;
19
+ export declare function getRuntimeTokenStoreFilePath(): string;
20
+ export declare function buildRuntimeTokenKey(userId: unknown, serverBaseUrl: unknown): string;
21
+ export declare function saveScopedRuntimeAccessToken(input: {
22
+ userId: string;
23
+ serverBaseUrl: string;
24
+ accessToken: string;
25
+ }): string;
26
+ export declare function getScopedRuntimeAccessToken(userId: unknown, serverBaseUrl: unknown): string | undefined;
18
27
  export declare function loadRuntimeBinding(): RuntimeBindingState;
19
28
  export declare function getRuntimeBindingPublicState(): Omit<RuntimeBindingState, "tokenHash" | "accessToken"> & {
20
29
  paired: boolean;
21
30
  };
22
31
  export declare function hasRuntimeBinding(): boolean;
23
32
  export declare function validateRuntimeBindingToken(token: unknown): boolean;
24
- export declare function getRuntimeAccessToken(): string | undefined;
33
+ export declare function getRuntimeAccessToken(userId?: unknown, serverBaseUrl?: unknown): string | undefined;
25
34
  export declare function validateRuntimePairingCode(code: unknown): boolean;
26
35
  export declare function saveRuntimeBinding(input: {
27
36
  accessToken: string;
28
37
  instanceId?: string;
38
+ userId?: string;
29
39
  schedulerBaseUrl?: string;
30
40
  }): RuntimeBindingState;
31
41
  export declare function clearRuntimeBinding(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-binding.d.ts","sourceRoot":"","sources":["../../src/services/runtime-binding.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAuCF,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wBAAgB,kBAAkB,IAAI,mBAAmB,CAWxD;AAED,wBAAgB,4BAA4B,IAAI,IAAI,CAClD,mBAAmB,EACnB,WAAW,GAAG,aAAa,CAC5B,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAWtB;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAG3C;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAYnE;AAED,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,SAAS,CAM1D;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAEjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,mBAAmB,CA6BtB;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAkB1C"}
1
+ {"version":3,"file":"runtime-binding.d.ts","sourceRoot":"","sources":["../../src/services/runtime-binding.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAoDF,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAErD;AAoBD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,OAAO,GACrB,MAAM,CAYR;AAsCD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,MAAM,CAWT;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,OAAO,EACf,aAAa,EAAE,OAAO,GACrB,MAAM,GAAG,SAAS,CAUpB;AAcD,wBAAgB,kBAAkB,IAAI,mBAAmB,CAWxD;AAED,wBAAgB,4BAA4B,IAAI,IAAI,CAClD,mBAAmB,EACnB,WAAW,GAAG,aAAa,CAC5B,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAWtB;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAG3C;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAcnE;AAED,wBAAgB,qBAAqB,CACnC,MAAM,CAAC,EAAE,OAAO,EAChB,aAAa,CAAC,EAAE,OAAO,GACtB,MAAM,GAAG,SAAS,CAWpB;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAEjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,mBAAmB,CAuCtB;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAkB1C"}
@@ -18,6 +18,9 @@ function bindingDir() {
18
18
  function bindingFile() {
19
19
  return path.join(bindingDir(), "binding.json");
20
20
  }
21
+ function tokenStoreFile() {
22
+ return path.join(bindingDir(), "runtime-tokens.properties");
23
+ }
21
24
  function hashToken(token) {
22
25
  return crypto
23
26
  .createHash(TOKEN_HASH_ALGORITHM)
@@ -32,6 +35,14 @@ function safeEqual(a, b) {
32
35
  }
33
36
  return crypto.timingSafeEqual(left, right);
34
37
  }
38
+ function safeTextEqual(a, b) {
39
+ const left = Buffer.from(a, "utf8");
40
+ const right = Buffer.from(b, "utf8");
41
+ if (left.length !== right.length) {
42
+ return false;
43
+ }
44
+ return crypto.timingSafeEqual(left, right);
45
+ }
35
46
  function normalizeToken(token) {
36
47
  return String(token || "").trim();
37
48
  }
@@ -41,6 +52,106 @@ export function getRuntimePairingCode() {
41
52
  export function getRuntimeBindingFilePath() {
42
53
  return bindingFile();
43
54
  }
55
+ export function getRuntimeTokenStoreFilePath() {
56
+ return tokenStoreFile();
57
+ }
58
+ function normalizeServerIp(serverBaseUrl) {
59
+ const raw = String(serverBaseUrl || "").trim();
60
+ if (!raw) {
61
+ return "";
62
+ }
63
+ try {
64
+ return new URL(raw).hostname.toLowerCase();
65
+ }
66
+ catch {
67
+ return raw
68
+ .replace(/^wss?:\/\//i, "")
69
+ .replace(/^https?:\/\//i, "")
70
+ .split("/")[0]
71
+ .split(":")[0]
72
+ .toLowerCase();
73
+ }
74
+ }
75
+ export function buildRuntimeTokenKey(userId, serverBaseUrl) {
76
+ const normalizedUserId = String(userId || "").trim();
77
+ const serverIp = normalizeServerIp(serverBaseUrl);
78
+ if (!normalizedUserId || !serverIp) {
79
+ throw new Error("userId and serverBaseUrl are required to build runtime token key");
80
+ }
81
+ return crypto
82
+ .createHash("md5")
83
+ .update(`${normalizedUserId}:${serverIp}`, "utf8")
84
+ .digest("hex");
85
+ }
86
+ function loadRuntimeTokenStore() {
87
+ const result = new Map();
88
+ try {
89
+ const content = fs.readFileSync(tokenStoreFile(), "utf8");
90
+ for (const line of content.split(/\r?\n/)) {
91
+ const trimmed = line.trim();
92
+ if (!trimmed || trimmed.startsWith("#")) {
93
+ continue;
94
+ }
95
+ const separatorIndex = trimmed.indexOf("=");
96
+ if (separatorIndex <= 0) {
97
+ continue;
98
+ }
99
+ const key = trimmed.slice(0, separatorIndex).trim();
100
+ const token = trimmed.slice(separatorIndex + 1).trim();
101
+ if (key && token) {
102
+ result.set(key, token);
103
+ }
104
+ }
105
+ }
106
+ catch {
107
+ // Missing token store is valid for an unpaired bridge.
108
+ }
109
+ return result;
110
+ }
111
+ function saveRuntimeTokenStore(tokens) {
112
+ fs.mkdirSync(bindingDir(), { recursive: true });
113
+ const lines = [...tokens.entries()]
114
+ .sort(([left], [right]) => left.localeCompare(right))
115
+ .map(([key, token]) => `${key}=${token}`);
116
+ fs.writeFileSync(tokenStoreFile(), `${lines.join("\n")}\n`, {
117
+ encoding: "utf8",
118
+ mode: 0o600,
119
+ });
120
+ }
121
+ export function saveScopedRuntimeAccessToken(input) {
122
+ const accessToken = normalizeToken(input.accessToken);
123
+ if (accessToken.length < 16) {
124
+ throw new Error("accessToken must be at least 16 characters");
125
+ }
126
+ const key = buildRuntimeTokenKey(input.userId, input.serverBaseUrl);
127
+ const tokens = loadRuntimeTokenStore();
128
+ tokens.set(key, accessToken);
129
+ saveRuntimeTokenStore(tokens);
130
+ return key;
131
+ }
132
+ export function getScopedRuntimeAccessToken(userId, serverBaseUrl) {
133
+ if (!userId || !serverBaseUrl) {
134
+ return undefined;
135
+ }
136
+ try {
137
+ const key = buildRuntimeTokenKey(userId, serverBaseUrl);
138
+ return loadRuntimeTokenStore().get(key);
139
+ }
140
+ catch {
141
+ return undefined;
142
+ }
143
+ }
144
+ function validateRuntimeTokenStore(token) {
145
+ if (!token) {
146
+ return false;
147
+ }
148
+ for (const storedToken of loadRuntimeTokenStore().values()) {
149
+ if (safeTextEqual(token, storedToken)) {
150
+ return true;
151
+ }
152
+ }
153
+ return false;
154
+ }
44
155
  export function loadRuntimeBinding() {
45
156
  try {
46
157
  const raw = fs.readFileSync(bindingFile(), "utf8");
@@ -72,12 +183,18 @@ export function validateRuntimeBindingToken(token) {
72
183
  return false;
73
184
  }
74
185
  const state = loadRuntimeBinding();
75
- if (state.status !== "paired" || !state.tokenHash) {
76
- return false;
186
+ if (state.status === "paired" && state.tokenHash) {
187
+ if (safeEqual(hashToken(candidate), state.tokenHash)) {
188
+ return true;
189
+ }
77
190
  }
78
- return safeEqual(hashToken(candidate), state.tokenHash);
191
+ return validateRuntimeTokenStore(candidate);
79
192
  }
80
- export function getRuntimeAccessToken() {
193
+ export function getRuntimeAccessToken(userId, serverBaseUrl) {
194
+ const scopedToken = getScopedRuntimeAccessToken(userId, serverBaseUrl);
195
+ if (scopedToken) {
196
+ return scopedToken;
197
+ }
81
198
  const state = loadRuntimeBinding();
82
199
  if (state.status !== "paired") {
83
200
  return undefined;
@@ -99,6 +216,7 @@ export function saveRuntimeBinding(input) {
99
216
  instanceId: input.instanceId
100
217
  ? String(input.instanceId)
101
218
  : previous.instanceId,
219
+ userId: input.userId ? String(input.userId) : previous.userId,
102
220
  schedulerBaseUrl: input.schedulerBaseUrl
103
221
  ? String(input.schedulerBaseUrl)
104
222
  : previous.schedulerBaseUrl,
@@ -112,6 +230,14 @@ export function saveRuntimeBinding(input) {
112
230
  encoding: "utf8",
113
231
  mode: 0o600,
114
232
  });
233
+ if (next.userId && next.schedulerBaseUrl) {
234
+ const key = saveScopedRuntimeAccessToken({
235
+ userId: next.userId,
236
+ serverBaseUrl: next.schedulerBaseUrl,
237
+ accessToken,
238
+ });
239
+ log.info(`[runtime-bridge] scoped runtime token saved: key=${key}`);
240
+ }
115
241
  log.info(`[runtime-bridge] runtime binding saved: ${bindingFile()}`);
116
242
  return next;
117
243
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",