aws-runtime-bridge 1.9.2 → 1.9.5

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.
@@ -4,6 +4,7 @@
4
4
  * 管理终端会话的输出缓冲和刷新机制
5
5
  */
6
6
  import axios from "axios";
7
+ import { requestRuntimeAccessTokenRefreshForServer } from "../services/auto-register.js";
7
8
  import { resolveSchedulerBaseUrlFrom } from "../config.js";
8
9
  import { logger } from "../utils/logger.js";
9
10
  import { getRuntimeAccessToken } from "./runtime-binding.js";
@@ -22,6 +23,42 @@ function resolveRuntimeCallbackBaseUrl(options) {
22
23
  }
23
24
  /** 输出刷新防抖时间:合并启动期高频小块输出,降低 HTTP/WebSocket 压力。 */
24
25
  const OUTPUT_FLUSH_DEBOUNCE_MS = 100;
26
+ function getAxiosStatusCode(error) {
27
+ if (typeof error !== "object" || error === null)
28
+ return undefined;
29
+ const response = error.response;
30
+ if (typeof response !== "object" || response === null)
31
+ return undefined;
32
+ const status = response.status;
33
+ return typeof status === "number" ? status : undefined;
34
+ }
35
+ /** 防止并发回调同时触发多次 token 刷新。 */
36
+ let tokenRefreshPromise = null;
37
+ async function tryRefreshRuntimeToken(serverBaseUrl) {
38
+ if (tokenRefreshPromise)
39
+ return tokenRefreshPromise;
40
+ tokenRefreshPromise = (async () => {
41
+ try {
42
+ const result = await requestRuntimeAccessTokenRefreshForServer(serverBaseUrl);
43
+ if (result.success && result.runtimeAccessToken) {
44
+ logger.info("[session-output] Runtime token refreshed after 401");
45
+ return true;
46
+ }
47
+ logger.warn("[session-output] Runtime token refresh failed", { error: result.error });
48
+ return false;
49
+ }
50
+ catch (error) {
51
+ logger.warn("[session-output] Runtime token refresh threw", { error });
52
+ return false;
53
+ }
54
+ })();
55
+ try {
56
+ return await tokenRefreshPromise;
57
+ }
58
+ finally {
59
+ tokenRefreshPromise = null;
60
+ }
61
+ }
25
62
  /** 单个会话待发送输出上限,避免调度器不可达时内存无限增长。 */
26
63
  const MAX_PENDING_OUTPUT_CHARS = 64 * 1024;
27
64
  /** 单次回调最大发送量,避免一次 POST 携带过大的终端噪声。 */
@@ -66,6 +103,23 @@ export async function sendStatus(agentId, sessionId, status, actionInfo, usage,
66
103
  return await axios.post(`${callbackBaseUrl}/api/runtime/callback/status`, payload, { headers });
67
104
  }
68
105
  catch (error) {
106
+ if (getAxiosStatusCode(error) === 401) {
107
+ const refreshed = await tryRefreshRuntimeToken(callbackBaseUrl);
108
+ if (refreshed) {
109
+ const retryHeaders = runtimeAuthHeaders(authOptions, callbackBaseUrl);
110
+ if (retryHeaders) {
111
+ try {
112
+ return await axios.post(`${callbackBaseUrl}/api/runtime/callback/status`, payload, { headers: retryHeaders });
113
+ }
114
+ catch (retryError) {
115
+ logger.warn("Runtime status callback retry failed after token refresh", {
116
+ agentId, sessionId, status, error: retryError,
117
+ });
118
+ return undefined;
119
+ }
120
+ }
121
+ }
122
+ }
69
123
  logger.warn("Runtime status callback failed", {
70
124
  agentId,
71
125
  sessionId,
@@ -85,8 +139,8 @@ export async function sendStatus(agentId, sessionId, status, actionInfo, usage,
85
139
  * @param seq - 序列号
86
140
  */
87
141
  export async function sendOutput(agentId, output, sessionId, seq, metadata, authOptions) {
142
+ const callbackBaseUrl = resolveRuntimeCallbackBaseUrl(authOptions);
88
143
  try {
89
- const callbackBaseUrl = resolveRuntimeCallbackBaseUrl(authOptions);
90
144
  const headers = runtimeAuthHeaders(authOptions, callbackBaseUrl);
91
145
  if (!headers) {
92
146
  logger.warn("Skip runtime output callback because runtime token is missing", {
@@ -99,6 +153,21 @@ export async function sendOutput(agentId, output, sessionId, seq, metadata, auth
99
153
  await axios.post(`${callbackBaseUrl}/api/runtime/callback/output`, { agentId, output, sessionId, seq, ...metadata }, { headers });
100
154
  }
101
155
  catch (error) {
156
+ if (getAxiosStatusCode(error) === 401) {
157
+ const refreshed = await tryRefreshRuntimeToken(callbackBaseUrl);
158
+ if (refreshed) {
159
+ const retryHeaders = runtimeAuthHeaders(authOptions, callbackBaseUrl);
160
+ if (retryHeaders) {
161
+ try {
162
+ await axios.post(`${callbackBaseUrl}/api/runtime/callback/output`, { agentId, output, sessionId, seq, ...metadata }, { headers: retryHeaders });
163
+ return;
164
+ }
165
+ catch {
166
+ // retry also failed
167
+ }
168
+ }
169
+ }
170
+ }
102
171
  logger.warn("Runtime output callback failed", {
103
172
  agentId,
104
173
  sessionId,
@@ -114,15 +183,31 @@ export async function sendOutput(agentId, output, sessionId, seq, metadata, auth
114
183
  * @param questions - 结构化问题列表
115
184
  */
116
185
  export async function sendQuestionRequest(agentId, sessionId, questions, authOptions) {
186
+ const callbackBaseUrl = resolveRuntimeCallbackBaseUrl(authOptions);
187
+ const payload = { agentId, sessionId, questions };
117
188
  try {
118
- const callbackBaseUrl = resolveRuntimeCallbackBaseUrl(authOptions);
119
189
  const headers = runtimeAuthHeaders(authOptions, callbackBaseUrl);
120
190
  if (!headers) {
121
191
  return;
122
192
  }
123
- await axios.post(`${callbackBaseUrl}/api/runtime/callback/question`, { agentId, sessionId, questions }, { headers });
193
+ await axios.post(`${callbackBaseUrl}/api/runtime/callback/question`, payload, { headers });
124
194
  }
125
- catch {
195
+ catch (error) {
196
+ if (getAxiosStatusCode(error) === 401) {
197
+ const refreshed = await tryRefreshRuntimeToken(callbackBaseUrl);
198
+ if (refreshed) {
199
+ const retryHeaders = runtimeAuthHeaders(authOptions, callbackBaseUrl);
200
+ if (retryHeaders) {
201
+ try {
202
+ await axios.post(`${callbackBaseUrl}/api/runtime/callback/question`, payload, { headers: retryHeaders });
203
+ return;
204
+ }
205
+ catch {
206
+ // retry also failed
207
+ }
208
+ }
209
+ }
210
+ }
126
211
  // ignore transient callback failures
127
212
  }
128
213
  }
@@ -130,15 +215,30 @@ export async function sendQuestionRequest(agentId, sessionId, questions, authOpt
130
215
  * 发送工作区文件变更事件到调度器,由调度器通过 WebSocket 通知前端刷新预览。
131
216
  */
132
217
  export async function sendFileChanged(payload) {
218
+ const callbackBaseUrl = resolveRuntimeCallbackBaseUrl();
133
219
  try {
134
- const callbackBaseUrl = resolveRuntimeCallbackBaseUrl();
135
220
  const headers = runtimeAuthHeaders(undefined, callbackBaseUrl);
136
221
  if (!headers) {
137
222
  return;
138
223
  }
139
224
  await axios.post(`${callbackBaseUrl}/api/runtime/callback/file-changed`, payload, { headers });
140
225
  }
141
- catch {
226
+ catch (error) {
227
+ if (getAxiosStatusCode(error) === 401) {
228
+ const refreshed = await tryRefreshRuntimeToken(callbackBaseUrl);
229
+ if (refreshed) {
230
+ const retryHeaders = runtimeAuthHeaders(undefined, callbackBaseUrl);
231
+ if (retryHeaders) {
232
+ try {
233
+ await axios.post(`${callbackBaseUrl}/api/runtime/callback/file-changed`, payload, { headers: retryHeaders });
234
+ return;
235
+ }
236
+ catch {
237
+ // retry also failed
238
+ }
239
+ }
240
+ }
241
+ }
142
242
  // ignore transient callback failures
143
243
  }
144
244
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.9.2",
3
+ "version": "1.9.5",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",