aws-runtime-bridge 1.9.70 → 1.9.71
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 +8 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +56 -12
- package/dist/index.js +14 -7
- package/dist/routes/file-browser.d.ts.map +1 -1
- package/dist/routes/file-browser.js +7 -10
- package/dist/routes/instance.js +5 -2
- package/dist/services/auto-register.d.ts +14 -0
- package/dist/services/auto-register.d.ts.map +1 -1
- package/dist/services/auto-register.js +195 -23
- package/dist/services/event-delta-pusher.js +1 -1
- package/dist/services/lifecycle-state.js +1 -1
- package/dist/services/mcp-launch-binding-queue.js +1 -1
- package/dist/services/orphan-monitor.d.ts +5 -0
- package/dist/services/orphan-monitor.d.ts.map +1 -1
- package/dist/services/orphan-monitor.js +48 -4
- package/dist/services/runtime-binding.d.ts +4 -5
- package/dist/services/runtime-binding.d.ts.map +1 -1
- package/dist/services/runtime-binding.js +14 -16
- package/dist/services/runtime-lifecycle-policy.d.ts +1 -0
- package/dist/services/runtime-lifecycle-policy.d.ts.map +1 -1
- package/dist/services/runtime-lifecycle-policy.js +1 -0
- package/dist/services/session-output.js +1 -1
- package/dist/services/tunnel-client.d.ts +21 -0
- package/dist/services/tunnel-client.d.ts.map +1 -0
- package/dist/services/tunnel-client.js +398 -0
- package/dist/services/workspaceTaskChangeWatcher.js +1 -1
- package/dist/utils/file-utils.js +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* 1. 环境变量
|
|
8
8
|
* 2. 用户目录下的 .aws-bridge/config.json 文件
|
|
9
9
|
*/
|
|
10
|
+
import { v4 as uuidv4 } from "uuid";
|
|
10
11
|
import axios from "axios";
|
|
11
12
|
import os from "node:os";
|
|
12
13
|
import path from "node:path";
|
|
@@ -29,6 +30,56 @@ let cachedConfiguredConnectionKeys = [];
|
|
|
29
30
|
export function getConfiguredConnectionKeys() {
|
|
30
31
|
return [...cachedConfiguredConnectionKeys];
|
|
31
32
|
}
|
|
33
|
+
/** 注册失败后的后台重试默认间隔(毫秒),可用 AWS_REGISTER_BACKGROUND_RETRY_INTERVAL 覆盖 */
|
|
34
|
+
const DEFAULT_BACKGROUND_RETRY_INTERVAL_MS = 30_000;
|
|
35
|
+
function getBackgroundRetryIntervalMs() {
|
|
36
|
+
const raw = Number(process.env.AWS_REGISTER_BACKGROUND_RETRY_INTERVAL);
|
|
37
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_BACKGROUND_RETRY_INTERVAL_MS;
|
|
38
|
+
}
|
|
39
|
+
/** 后台重试定时器:key = 归一化 serverUrl,保证同一 target 只有一个重试循环 */
|
|
40
|
+
const backgroundRetryTimers = new Map();
|
|
41
|
+
/**
|
|
42
|
+
* 调度后台注册重试。
|
|
43
|
+
* bridge 先于 server 启动(或 server 临时不可用)时,前台重试耗尽后转入后台
|
|
44
|
+
* 持续重试,直到与调度中心建立连接为止。
|
|
45
|
+
*/
|
|
46
|
+
function scheduleBackgroundRegisterRetry(config) {
|
|
47
|
+
const rawServerUrl = config.serverUrl || schedulerBaseUrl;
|
|
48
|
+
const key = normalizeSchedulerBaseUrl(rawServerUrl) || rawServerUrl;
|
|
49
|
+
if (backgroundRetryTimers.has(key)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const timer = setTimeout(() => {
|
|
53
|
+
backgroundRetryTimers.delete(key);
|
|
54
|
+
void retryRegisterInBackground(config, key);
|
|
55
|
+
}, getBackgroundRetryIntervalMs());
|
|
56
|
+
// unref:后台重试不应阻止进程退出(优雅关闭/测试进程)
|
|
57
|
+
timer.unref?.();
|
|
58
|
+
backgroundRetryTimers.set(key, timer);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 后台重试注册:每次触发时重新从 config.json 读取该 target 的最新配置
|
|
62
|
+
*(运行期间修改配置可生效)。失败由 autoRegisterSingle 末尾自动调度下一次;
|
|
63
|
+
* 配置被禁用/删除(enabled=false 或无 userKey)时自然终止重试循环。
|
|
64
|
+
*/
|
|
65
|
+
async function retryRegisterInBackground(config, key) {
|
|
66
|
+
try {
|
|
67
|
+
const fresh = loadConfigs().find((item) => (normalizeSchedulerBaseUrl(item.serverUrl || schedulerBaseUrl) ||
|
|
68
|
+
(item.serverUrl || schedulerBaseUrl)) === key) || config;
|
|
69
|
+
await autoRegisterSingle(fresh, { backgroundRetry: true });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
logger.warn(`[AutoRegister] 后台重试注册异常: ${error.message}`);
|
|
73
|
+
scheduleBackgroundRegisterRetry(config);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** 停止所有后台注册重试(优雅关闭/测试清理用) */
|
|
77
|
+
export function cancelBackgroundRegisterRetries() {
|
|
78
|
+
for (const timer of backgroundRetryTimers.values()) {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
}
|
|
81
|
+
backgroundRetryTimers.clear();
|
|
82
|
+
}
|
|
32
83
|
function normalizeOptionalString(value) {
|
|
33
84
|
if (value == null) {
|
|
34
85
|
return undefined;
|
|
@@ -73,12 +124,14 @@ function normalizeTargetConfig(source) {
|
|
|
73
124
|
userKey: normalizeOptionalString(source.userKey),
|
|
74
125
|
connectionKey,
|
|
75
126
|
instanceName: normalizeOptionalString(source.instanceName),
|
|
127
|
+
instanceId: normalizeOptionalString(source.instanceId),
|
|
76
128
|
projectName: normalizeOptionalString(source.projectName),
|
|
77
129
|
roleName: normalizeOptionalString(source.roleName),
|
|
78
130
|
workspacePath: normalizeOptionalString(source.workspacePath),
|
|
79
131
|
registerAddr: normalizeOptionalString(source.registerAddr),
|
|
80
132
|
registerIp: normalizeOptionalString(source.registerIp),
|
|
81
133
|
virtualIp: normalizeOptionalString(source.virtualIp),
|
|
134
|
+
tunnel: normalizeOptionalBoolean(source.tunnel),
|
|
82
135
|
preferSameLanIp: normalizeOptionalBoolean(source.preferSameLanIp),
|
|
83
136
|
maxRetries: normalizeOptionalNumber(source.maxRetries),
|
|
84
137
|
retryInterval: normalizeOptionalNumber(source.retryInterval),
|
|
@@ -263,12 +316,15 @@ export function loadConfig() {
|
|
|
263
316
|
userKey: merged.userKey || "",
|
|
264
317
|
connectionKey: merged.connectionKey,
|
|
265
318
|
instanceName,
|
|
319
|
+
instanceId: merged.instanceId,
|
|
266
320
|
projectName: merged.projectName,
|
|
267
321
|
roleName: merged.roleName,
|
|
268
322
|
workspacePath: merged.workspacePath,
|
|
269
323
|
registerAddr: merged.registerAddr,
|
|
270
324
|
registerIp: merged.registerIp,
|
|
271
325
|
virtualIp: merged.virtualIp,
|
|
326
|
+
// 保留三态(true/false/undefined):isTunnelConfig 依赖 undefined 走向后兼容判定
|
|
327
|
+
tunnel: typeof merged.tunnel === "boolean" ? merged.tunnel : undefined,
|
|
272
328
|
preferSameLanIp: merged.preferSameLanIp !== false,
|
|
273
329
|
maxRetries: merged.maxRetries,
|
|
274
330
|
retryInterval: merged.retryInterval,
|
|
@@ -298,12 +354,15 @@ export function loadConfigs() {
|
|
|
298
354
|
userKey: merged.userKey || "",
|
|
299
355
|
connectionKey: merged.connectionKey,
|
|
300
356
|
instanceName: merged.instanceName || os.hostname(),
|
|
357
|
+
instanceId: merged.instanceId,
|
|
301
358
|
projectName: merged.projectName,
|
|
302
359
|
roleName: merged.roleName,
|
|
303
360
|
workspacePath: merged.workspacePath,
|
|
304
361
|
registerAddr: merged.registerAddr,
|
|
305
362
|
registerIp: merged.registerIp,
|
|
306
363
|
virtualIp: merged.virtualIp,
|
|
364
|
+
// 保留三态(true/false/undefined):isTunnelConfig 依赖 undefined 走向后兼容判定
|
|
365
|
+
tunnel: typeof merged.tunnel === "boolean" ? merged.tunnel : undefined,
|
|
307
366
|
preferSameLanIp: merged.preferSameLanIp !== false,
|
|
308
367
|
maxRetries: merged.maxRetries,
|
|
309
368
|
retryInterval: merged.retryInterval,
|
|
@@ -421,8 +480,45 @@ function getPreferredIpAddress(schedulerUrl) {
|
|
|
421
480
|
function getLocalIpAddress(targetServerUrl = schedulerBaseUrl) {
|
|
422
481
|
return getPreferredIpAddress(targetServerUrl);
|
|
423
482
|
}
|
|
483
|
+
export function isTunnelConfig(config) {
|
|
484
|
+
const check = config || loadConfig();
|
|
485
|
+
if (!check.enabled || !check.userKey) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
// 显式 tunnel=true → 隧道模式
|
|
489
|
+
if (check.tunnel === true) {
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
// 显式 tunnel=false → 直连模式(用于 loopback 等不需要隧道的场景)
|
|
493
|
+
if (check.tunnel === false) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
// 未显式设置 tunnel:保持向后兼容——无 registerAddr/registerIp/virtualIp 即为隧道模式。
|
|
497
|
+
// 隧道模式下 bridge 位于 NAT/内网,没有可直接访问的地址,由 server 反向代理。
|
|
498
|
+
return !check.registerAddr && !check.registerIp && !check.virtualIp;
|
|
499
|
+
}
|
|
424
500
|
function resolveRuntimeBridgeBaseUrl(config) {
|
|
425
501
|
const port = process.env.AWS_RUNTIME_BRIDGE_PORT || 18081;
|
|
502
|
+
// 隧道模式:bridge 位于 NAT/内网,通过 WebSocket 反向连出到 server,
|
|
503
|
+
// server 经 /tunnel/<instanceId>/ 虚拟路径反向调用 bridge。
|
|
504
|
+
// 触发条件见 isTunnelConfig():显式 tunnel=true,或未填地址且未显式 tunnel=false。
|
|
505
|
+
// 此时 config.instanceId 必须已由 autoRegisterSingle 预声明(见 callers)。
|
|
506
|
+
if (isTunnelConfig(config)) {
|
|
507
|
+
const targetServerUrl = config.serverUrl || schedulerBaseUrl;
|
|
508
|
+
try {
|
|
509
|
+
const url = new URL(targetServerUrl);
|
|
510
|
+
// 隧道模式下 instanceId 必须已设,否则视为配置错误,降级到 localIp
|
|
511
|
+
if (!config.instanceId) {
|
|
512
|
+
logger.warn("[AutoRegister] 隧道模式但缺少 instanceId,降级为直连模式");
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
return `${url.protocol}//${url.host}/tunnel/${config.instanceId}`;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
catch {
|
|
519
|
+
logger.warn("[AutoRegister] serverUrl 解析失败,降级为直连模式");
|
|
520
|
+
}
|
|
521
|
+
}
|
|
426
522
|
const localIp = config.preferSameLanIp === false
|
|
427
523
|
? "127.0.0.1"
|
|
428
524
|
: getLocalIpAddress(config.serverUrl || schedulerBaseUrl);
|
|
@@ -436,13 +532,10 @@ function resolveRuntimeBridgeBaseUrl(config) {
|
|
|
436
532
|
return raw;
|
|
437
533
|
}
|
|
438
534
|
// 未带协议:统一补 http://;末尾无 :port 时补默认端口
|
|
439
|
-
// - IPv4/host:port → 原样使用
|
|
440
|
-
// - 纯 IPv4/host → 追加默认端口
|
|
441
|
-
// - IPv6 必须用完整 URL 形式(http://[::1]:port)才能正确解析
|
|
442
535
|
const hasTrailingPort = /:[0-9]+$/.test(raw);
|
|
443
536
|
return hasTrailingPort ? `http://${raw}` : `http://${raw}:${port}`;
|
|
444
537
|
}
|
|
445
|
-
function loadPrimaryLifecycleConfig() {
|
|
538
|
+
export function loadPrimaryLifecycleConfig() {
|
|
446
539
|
return loadConfigs()[0] || loadConfig();
|
|
447
540
|
}
|
|
448
541
|
function resolveRegisteredSchedulerBaseUrl(instanceId) {
|
|
@@ -479,6 +572,7 @@ async function doRegister(config) {
|
|
|
479
572
|
const request = {
|
|
480
573
|
tenantId: config.tenantId,
|
|
481
574
|
userKey: config.userKey,
|
|
575
|
+
instanceId: config.instanceId,
|
|
482
576
|
instanceName,
|
|
483
577
|
hostname: os.hostname(),
|
|
484
578
|
virtualIp: config.registerAddr || config.registerIp || config.virtualIp,
|
|
@@ -500,13 +594,61 @@ async function doRegister(config) {
|
|
|
500
594
|
});
|
|
501
595
|
return response.data;
|
|
502
596
|
}
|
|
597
|
+
/** 串行化 config.json 回写,避免并行注册时并发写入冲突 */
|
|
598
|
+
let configWriteBackChain = Promise.resolve();
|
|
599
|
+
/**
|
|
600
|
+
* 将 server 返回的 instanceId 回写到 config.json 对应 target。
|
|
601
|
+
* 回写策略:读 → 找到 serverUrl 匹配的 target → 更新 instanceId → 写回。
|
|
602
|
+
* 仅在值变化时写入,避免不必要的文件 I/O。多 target 并行注册时通过 Promise 链串行化。
|
|
603
|
+
*/
|
|
604
|
+
function writeBackInstanceInfoToConfig(targetServerUrl, instanceId) {
|
|
605
|
+
const task = configWriteBackChain.then(() => {
|
|
606
|
+
const configPath = getAutoRegisterConfigFilePath();
|
|
607
|
+
if (!fs.existsSync(configPath)) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
611
|
+
const config = JSON.parse(raw);
|
|
612
|
+
const normalizedTargetUrl = normalizeSchedulerBaseUrl(targetServerUrl);
|
|
613
|
+
if (!normalizedTargetUrl) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
let changed = false;
|
|
617
|
+
const updateTarget = (target) => {
|
|
618
|
+
const url = normalizeSchedulerBaseUrl(String(target.serverUrl || target.schedulerBaseUrl || ""));
|
|
619
|
+
if (url !== normalizedTargetUrl) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (instanceId && target.instanceId !== instanceId) {
|
|
623
|
+
target.instanceId = instanceId;
|
|
624
|
+
changed = true;
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
if (Array.isArray(config.autoRegisterTargets)) {
|
|
628
|
+
for (const target of config.autoRegisterTargets) {
|
|
629
|
+
if (target && typeof target === "object" && !Array.isArray(target)) {
|
|
630
|
+
updateTarget(target);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
updateTarget(config);
|
|
636
|
+
}
|
|
637
|
+
if (changed) {
|
|
638
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
639
|
+
logger.info(`[AutoRegister] 已回写 instanceId 到 config.json (serverUrl=${targetServerUrl})`);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
configWriteBackChain = task.catch(() => { });
|
|
643
|
+
return task;
|
|
644
|
+
}
|
|
503
645
|
/**
|
|
504
646
|
* 执行注销请求
|
|
505
647
|
*/
|
|
506
648
|
async function doUnregister(instanceId) {
|
|
507
649
|
const targetServerUrl = resolveLifecycleSchedulerBaseUrl(loadPrimaryLifecycleConfig(), instanceId);
|
|
508
650
|
const binding = loadRuntimeBinding();
|
|
509
|
-
const runtimeAccessToken = getRuntimeAccessToken(
|
|
651
|
+
const runtimeAccessToken = getRuntimeAccessToken(targetServerUrl);
|
|
510
652
|
if (!runtimeAccessToken) {
|
|
511
653
|
throw new Error("runtimeAccessToken is required to unregister bridge instance");
|
|
512
654
|
}
|
|
@@ -540,7 +682,7 @@ export async function autoRegister(customConfig) {
|
|
|
540
682
|
}
|
|
541
683
|
return autoRegisterSingle(configs[0] || loadConfig());
|
|
542
684
|
}
|
|
543
|
-
async function autoRegisterSingle(config) {
|
|
685
|
+
async function autoRegisterSingle(config, options) {
|
|
544
686
|
// 检查是否启用
|
|
545
687
|
if (!config.enabled) {
|
|
546
688
|
logger.info("[AutoRegister] 自动注册未启用");
|
|
@@ -552,8 +694,16 @@ async function autoRegisterSingle(config) {
|
|
|
552
694
|
logger.error("[AutoRegister] 缺少用户 Key(在配置文件中设置 userKey 或设置环境变量 AWS_USER_KEY)");
|
|
553
695
|
return false;
|
|
554
696
|
}
|
|
697
|
+
// ★ 隧道模式:在注册前预声明 instanceId,确保 URL 与注册 request 中的 instanceId 一致
|
|
698
|
+
const isTunnel = isTunnelConfig(config);
|
|
699
|
+
if (isTunnel && !config.instanceId) {
|
|
700
|
+
// 用 uuid 前 12 位确保唯一可读
|
|
701
|
+
config = { ...config, instanceId: `bridge-${uuidv4().replace(/-/g, "").slice(0, 12)}` };
|
|
702
|
+
logger.info(`[AutoRegister] 隧道模式:已预声明 instanceId=${config.instanceId}`);
|
|
703
|
+
}
|
|
555
704
|
// tenantId 现在是可选的,如果没有提供,后端会根据 userKey 自动推导
|
|
556
|
-
|
|
705
|
+
// 后台重试每轮只尝试 1 次,避免每个间隔内刷出一串重试日志
|
|
706
|
+
const maxRetries = options?.backgroundRetry ? 1 : (config.maxRetries ?? 3);
|
|
557
707
|
const retryInterval = config.retryInterval ?? 2000;
|
|
558
708
|
// 重试循环
|
|
559
709
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
@@ -581,6 +731,12 @@ async function autoRegisterSingle(config) {
|
|
|
581
731
|
schedulerBaseUrl: response.serverUrl || config.serverUrl || schedulerBaseUrl,
|
|
582
732
|
});
|
|
583
733
|
logger.info("[AutoRegister] 已保存调度中心下发的 runtime access token");
|
|
734
|
+
// 回写 instanceId 到 config.json,供后续 instanceId 匹配使用
|
|
735
|
+
if (response.instanceId) {
|
|
736
|
+
writeBackInstanceInfoToConfig(config.serverUrl || schedulerBaseUrl, response.instanceId).catch((error) => {
|
|
737
|
+
logger.warn(`[AutoRegister] 回写 instanceId 到 config.json 失败: ${error.message}`);
|
|
738
|
+
});
|
|
739
|
+
}
|
|
584
740
|
}
|
|
585
741
|
catch (error) {
|
|
586
742
|
const err = error;
|
|
@@ -592,6 +748,15 @@ async function autoRegisterSingle(config) {
|
|
|
592
748
|
else {
|
|
593
749
|
logger.warn("[AutoRegister] 注册响应未包含 runtime access token,MCP 将无法访问调度中心,请检查 server 的 bridge 实例 token 颁发逻辑");
|
|
594
750
|
}
|
|
751
|
+
// ★ 隧道模式:注册成功且拿到 runtimeAccessToken 后,使用 server 返回的真实 instanceId 启动隧道客户端。
|
|
752
|
+
// 没有 runtimeAccessToken 时隧道鉴权必然失败,跳过启动以免反复重连刷日志。
|
|
753
|
+
// 多 target 场景:传入本次注册的 serverUrl,隧道客户端按 server 维度多实例并存。
|
|
754
|
+
if (isTunnel && response.instanceId && response.runtimeAccessToken) {
|
|
755
|
+
const { startTunnelClient } = await import("./tunnel-client.js");
|
|
756
|
+
const tunnelServerUrl = config.serverUrl || schedulerBaseUrl;
|
|
757
|
+
startTunnelClient(tunnelServerUrl, response.instanceId);
|
|
758
|
+
logger.info(`[AutoRegister] 隧道客户端已启动: server=${tunnelServerUrl}, instanceId=${response.instanceId}`);
|
|
759
|
+
}
|
|
595
760
|
return true;
|
|
596
761
|
}
|
|
597
762
|
else {
|
|
@@ -613,17 +778,26 @@ async function autoRegisterSingle(config) {
|
|
|
613
778
|
await new Promise((resolve) => setTimeout(resolve, retryInterval));
|
|
614
779
|
}
|
|
615
780
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
781
|
+
if (options?.backgroundRetry) {
|
|
782
|
+
// 后台重试轮:单行日志,避免每个间隔刷一大段错误块
|
|
783
|
+
logger.warn(`[AutoRegister] 后台重试注册仍未成功 (server=${config.serverUrl || schedulerBaseUrl}): ${registrationState.error || "未知"}`);
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
logger.error(`[AutoRegister] ✗ 注册失败,已达最大重试次数`);
|
|
787
|
+
logger.error(`[AutoRegister] ========================================`);
|
|
788
|
+
logger.error(`[AutoRegister] 自动注册已启用但注册失败,实例暂时无法正常工作`);
|
|
789
|
+
logger.error(`[AutoRegister] 请检查以下配置:`);
|
|
790
|
+
logger.error(`[AutoRegister] - 用户 Key (userKey): ${config.userKey ? "****" + config.userKey.slice(-4) : "(未设置)"}`);
|
|
791
|
+
logger.error(`[AutoRegister] - 租户 ID (tenantId): ${config.tenantId || "(可选,将自动推导)"}`);
|
|
792
|
+
logger.error(`[AutoRegister] - 实例名 (instanceName): ${config.instanceName || "(使用主机名)"}`);
|
|
793
|
+
logger.error(`[AutoRegister] - 调度中心地址: ${config.serverUrl || schedulerBaseUrl}`);
|
|
794
|
+
logger.error(`[AutoRegister] - 最后错误: ${registrationState.error || "未知"}`);
|
|
795
|
+
logger.error(`[AutoRegister] ========================================`);
|
|
796
|
+
logger.error(`[AutoRegister] 配置来源:~/.aws-bridge/config.json 或环境变量`);
|
|
797
|
+
logger.warn(`[AutoRegister] 将在后台持续重试注册(每 ${Math.round(getBackgroundRetryIntervalMs() / 1000)} 秒),直到与调度中心建立连接`);
|
|
798
|
+
}
|
|
799
|
+
// 未与 server 建立连接前持续后台重试;成功/配置失效(禁用、缺 userKey)时自动停止
|
|
800
|
+
scheduleBackgroundRegisterRetry(config);
|
|
627
801
|
return false;
|
|
628
802
|
}
|
|
629
803
|
/**
|
|
@@ -716,11 +890,9 @@ export async function requestRuntimeAccessTokenRefreshForServer(serverBaseUrl) {
|
|
|
716
890
|
"scheduler did not return runtimeAccessToken",
|
|
717
891
|
};
|
|
718
892
|
}
|
|
719
|
-
const
|
|
720
|
-
const previousToken = getRuntimeAccessToken(userId, targetServerUrl);
|
|
893
|
+
const previousToken = getRuntimeAccessToken(targetServerUrl);
|
|
721
894
|
const updated = previousToken !== response.data.runtimeAccessToken;
|
|
722
895
|
saveScopedRuntimeAccessToken({
|
|
723
|
-
userId,
|
|
724
896
|
serverBaseUrl: targetServerUrl,
|
|
725
897
|
accessToken: response.data.runtimeAccessToken,
|
|
726
898
|
});
|
|
@@ -749,8 +921,8 @@ export async function unregister() {
|
|
|
749
921
|
logger.info(`[AutoRegister] ✓ 注销成功: ${registrationState.instanceId}`);
|
|
750
922
|
const targetServerUrl = resolveLifecycleSchedulerBaseUrl(loadPrimaryLifecycleConfig(), registrationState.instanceId);
|
|
751
923
|
const binding = loadRuntimeBinding();
|
|
752
|
-
if (binding.
|
|
753
|
-
clearScopedRuntimeAccessToken(
|
|
924
|
+
if (binding.schedulerBaseUrl) {
|
|
925
|
+
clearScopedRuntimeAccessToken(targetServerUrl);
|
|
754
926
|
}
|
|
755
927
|
clearRuntimeBinding();
|
|
756
928
|
registrationState.registered = false;
|
|
@@ -12,7 +12,7 @@ const pushStates = new Map();
|
|
|
12
12
|
function runtimeAuthHeaders(authOptions, callbackBaseUrl) {
|
|
13
13
|
const resolvedBaseUrl = callbackBaseUrl ?? resolveSchedulerBaseUrlFrom(authOptions?.schedulerBaseUrl).url;
|
|
14
14
|
const token = authOptions?.runtimeAccessToken ||
|
|
15
|
-
getRuntimeAccessToken(
|
|
15
|
+
getRuntimeAccessToken(resolvedBaseUrl) ||
|
|
16
16
|
getRuntimeAccessToken();
|
|
17
17
|
return token ? { "X-Runtime-Token": token } : undefined;
|
|
18
18
|
}
|
|
@@ -66,7 +66,7 @@ async function sendLifecycleNotification(operation) {
|
|
|
66
66
|
const schedulerBaseUrl = normalizeSchedulerBaseUrl(operation.schedulerBaseUrl) ||
|
|
67
67
|
normalizeSchedulerBaseUrl(loadRuntimeBinding().schedulerBaseUrl) ||
|
|
68
68
|
resolveSchedulerBaseUrlFrom(undefined).url;
|
|
69
|
-
const token = getRuntimeAccessToken(
|
|
69
|
+
const token = getRuntimeAccessToken(schedulerBaseUrl);
|
|
70
70
|
if (!token) {
|
|
71
71
|
throw new Error("runtime access token is missing");
|
|
72
72
|
}
|
|
@@ -126,7 +126,7 @@ export function claimMcpLaunchBinding(request) {
|
|
|
126
126
|
const runtimeState = loadRuntimeBinding();
|
|
127
127
|
const userId = binding.userId || runtimeState.userId;
|
|
128
128
|
const schedulerBaseUrl = binding.schedulerBaseUrl || runtimeState.schedulerBaseUrl;
|
|
129
|
-
const runtimeAccessToken = binding.runtimeAccessToken || getRuntimeAccessToken(
|
|
129
|
+
const runtimeAccessToken = binding.runtimeAccessToken || getRuntimeAccessToken(schedulerBaseUrl);
|
|
130
130
|
return {
|
|
131
131
|
...binding,
|
|
132
132
|
runtimeAccessToken,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orphan-monitor.d.ts","sourceRoot":"","sources":["../../src/services/orphan-monitor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,iBAAiB,GACjB,gBAAgB,GAChB,sBAAsB,GACtB,kBAAkB,GAClB,qBAAqB,GACrB,cAAc,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,cAAc,CAA2C;IACjE,OAAO,CAAC,OAAO,CAAkB;IAEjC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB5B;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA0E3B;;OAEG;YACW,oBAAoB;
|
|
1
|
+
{"version":3,"file":"orphan-monitor.d.ts","sourceRoot":"","sources":["../../src/services/orphan-monitor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,iBAAiB,GACjB,gBAAgB,GAChB,sBAAsB,GACtB,kBAAkB,GAClB,qBAAqB,GACrB,cAAc,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,cAAc,CAA2C;IACjE,OAAO,CAAC,OAAO,CAAkB;IAEjC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB5B;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA0E3B;;OAEG;YACW,oBAAoB;IA8ClC;;OAEG;YACW,iBAAiB;IAwC/B;;OAEG;YACW,eAAe;IA0B7B;;OAEG;YACW,mBAAmB;IAsBjC;;;OAGG;YACW,wBAAwB;IAmCtC;;OAEG;YACW,iBAAiB;IA8C/B;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAIrD;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAOtD;;OAEG;IACH,OAAO,CAAC,IAAI;IAUZ;;OAEG;IACH,SAAS,IAAI;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAO3E;AAKD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAKhD;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAGxD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC"}
|
|
@@ -127,7 +127,16 @@ export class OrphanMonitor {
|
|
|
127
127
|
};
|
|
128
128
|
this.emit(event);
|
|
129
129
|
logger.warn(`检测到孤儿进程: agentId=${agentId}, pid=${pid}`);
|
|
130
|
-
//
|
|
130
|
+
// ★ 优先向 server 确认 agent 是否已被删除。如果已删除,直接自动终止残留进程,
|
|
131
|
+
// 绕过 ORPHAN_AUTO_CLEAN_MODE 和双重开关保护——因为 agent 已明确不存在,
|
|
132
|
+
// 残留进程不可能再被恢复,继续运行只会浪费资源。
|
|
133
|
+
const agentExists = await this.checkAgentExistsOnServer(agentId);
|
|
134
|
+
if (agentExists === false) {
|
|
135
|
+
logger.info(`agent ${agentId} 已从服务端删除,自动终止残留进程 pid=${pid}`);
|
|
136
|
+
await this.autoCleanOrphan(agentId, pid);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// agent 仍存在或无法确认时,按原有模式处理
|
|
131
140
|
switch (ORPHAN_AUTO_CLEAN_MODE) {
|
|
132
141
|
case 'auto':
|
|
133
142
|
if (shouldAutoCleanOrphanProcesses()) {
|
|
@@ -229,6 +238,41 @@ export class OrphanMonitor {
|
|
|
229
238
|
await savePersistedSessions(aliveSessions);
|
|
230
239
|
}
|
|
231
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* 向 server 查询 agent 是否仍然存在。
|
|
243
|
+
* 返回 true=存在, false=已删除, null=无法确认(网络错误/认证失败等)。
|
|
244
|
+
*/
|
|
245
|
+
async checkAgentExistsOnServer(agentId) {
|
|
246
|
+
if (this.stopped) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const runtimeAccessToken = getRuntimeAccessToken(schedulerBaseUrl) || getRuntimeAccessToken();
|
|
251
|
+
if (!runtimeAccessToken) {
|
|
252
|
+
logger.debug('缺少 runtimeAccessToken,无法查询 agent 存在性');
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
const response = await fetch(`${schedulerBaseUrl}/api/runtime/callback/agent-exists`, {
|
|
256
|
+
method: 'POST',
|
|
257
|
+
headers: {
|
|
258
|
+
'Content-Type': 'application/json',
|
|
259
|
+
'X-Runtime-Token': runtimeAccessToken,
|
|
260
|
+
},
|
|
261
|
+
body: JSON.stringify({ agentId }),
|
|
262
|
+
signal: AbortSignal.timeout(5000),
|
|
263
|
+
});
|
|
264
|
+
if (!response.ok) {
|
|
265
|
+
logger.debug(`查询 agent 存在性失败: ${response.status}`);
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
const data = await response.json();
|
|
269
|
+
return data.exists === true;
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
logger.debug('查询 agent 存在性异常:', error);
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
232
276
|
/**
|
|
233
277
|
* 上报事件到调度中心
|
|
234
278
|
*/
|
|
@@ -239,16 +283,16 @@ export class OrphanMonitor {
|
|
|
239
283
|
return;
|
|
240
284
|
}
|
|
241
285
|
try {
|
|
242
|
-
const runtimeAccessToken = getRuntimeAccessToken(
|
|
286
|
+
const runtimeAccessToken = getRuntimeAccessToken(schedulerBaseUrl) || getRuntimeAccessToken();
|
|
243
287
|
if (!runtimeAccessToken) {
|
|
244
288
|
logger.debug(`缺少 runtimeAccessToken,跳过上报: ${eventType}`);
|
|
245
289
|
return;
|
|
246
290
|
}
|
|
247
|
-
const response = await fetch(`${schedulerBaseUrl}/api/runtime/orphan-event`, {
|
|
291
|
+
const response = await fetch(`${schedulerBaseUrl}/api/runtime/callback/orphan-event`, {
|
|
248
292
|
method: 'POST',
|
|
249
293
|
headers: {
|
|
250
294
|
'Content-Type': 'application/json',
|
|
251
|
-
'
|
|
295
|
+
'X-Runtime-Token': runtimeAccessToken,
|
|
252
296
|
},
|
|
253
297
|
body: JSON.stringify({
|
|
254
298
|
agentId,
|
|
@@ -18,21 +18,20 @@ export declare function normalizeSchedulerBaseUrl(value: unknown): string | unde
|
|
|
18
18
|
export declare function getRuntimePairingCode(): string;
|
|
19
19
|
export declare function getRuntimeBindingFilePath(): string;
|
|
20
20
|
export declare function getRuntimeTokenStoreFilePath(): string;
|
|
21
|
-
export declare function buildRuntimeTokenKey(
|
|
21
|
+
export declare function buildRuntimeTokenKey(serverBaseUrl: unknown): string;
|
|
22
22
|
export declare function saveScopedRuntimeAccessToken(input: {
|
|
23
|
-
userId: string;
|
|
24
23
|
serverBaseUrl: string;
|
|
25
24
|
accessToken: string;
|
|
26
25
|
}): string;
|
|
27
|
-
export declare function getScopedRuntimeAccessToken(
|
|
28
|
-
export declare function clearScopedRuntimeAccessToken(
|
|
26
|
+
export declare function getScopedRuntimeAccessToken(serverBaseUrl: unknown): string | undefined;
|
|
27
|
+
export declare function clearScopedRuntimeAccessToken(serverBaseUrl: unknown): boolean;
|
|
29
28
|
export declare function loadRuntimeBinding(): RuntimeBindingState;
|
|
30
29
|
export declare function getRuntimeBindingPublicState(): Omit<RuntimeBindingState, "tokenHash" | "accessToken"> & {
|
|
31
30
|
paired: boolean;
|
|
32
31
|
};
|
|
33
32
|
export declare function hasRuntimeBinding(): boolean;
|
|
34
33
|
export declare function validateRuntimeBindingToken(token: unknown): boolean;
|
|
35
|
-
export declare function getRuntimeAccessToken(
|
|
34
|
+
export declare function getRuntimeAccessToken(serverBaseUrl?: unknown): string | undefined;
|
|
36
35
|
export declare function validateRuntimePairingCode(code: unknown): boolean;
|
|
37
36
|
export declare function saveRuntimeBinding(input: {
|
|
38
37
|
accessToken: string;
|
|
@@ -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,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;AAmEF,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAqB5E;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAErD;AAiCD,wBAAgB,oBAAoB,CAClC,
|
|
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;AAmEF,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAqB5E;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAErD;AAiCD,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,OAAO,GACrB,MAAM,CAWR;AAsCD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,MAAM,CAWT;AAED,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,OAAO,GACrB,MAAM,GAAG,SAAS,CAUpB;AAED,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,OAAO,GACrB,OAAO,CAeT;AAcD,wBAAgB,kBAAkB,IAAI,mBAAmB,CAgBxD;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,aAAa,CAAC,EAAE,OAAO,GACtB,MAAM,GAAG,SAAS,CAyBpB;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;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CA0B3C"}
|
|
@@ -117,15 +117,14 @@ function normalizeRuntimeTokenServerUrl(serverBaseUrl) {
|
|
|
117
117
|
}
|
|
118
118
|
return raw.replace(/\/+$/, "").toLowerCase();
|
|
119
119
|
}
|
|
120
|
-
export function buildRuntimeTokenKey(
|
|
121
|
-
const normalizedUserId = String(userId || "").trim();
|
|
120
|
+
export function buildRuntimeTokenKey(serverBaseUrl) {
|
|
122
121
|
const serverUrl = normalizeRuntimeTokenServerUrl(serverBaseUrl);
|
|
123
|
-
if (!
|
|
124
|
-
throw new Error("
|
|
122
|
+
if (!serverUrl) {
|
|
123
|
+
throw new Error("serverBaseUrl is required to build runtime token key");
|
|
125
124
|
}
|
|
126
125
|
return crypto
|
|
127
126
|
.createHash("md5")
|
|
128
|
-
.update(
|
|
127
|
+
.update(serverUrl, "utf8")
|
|
129
128
|
.digest("hex");
|
|
130
129
|
}
|
|
131
130
|
function loadRuntimeTokenStore() {
|
|
@@ -168,30 +167,30 @@ export function saveScopedRuntimeAccessToken(input) {
|
|
|
168
167
|
if (accessToken.length < 16) {
|
|
169
168
|
throw new Error("accessToken must be at least 16 characters");
|
|
170
169
|
}
|
|
171
|
-
const key = buildRuntimeTokenKey(input.
|
|
170
|
+
const key = buildRuntimeTokenKey(input.serverBaseUrl);
|
|
172
171
|
const tokens = loadRuntimeTokenStore();
|
|
173
172
|
tokens.set(key, accessToken);
|
|
174
173
|
saveRuntimeTokenStore(tokens);
|
|
175
174
|
return key;
|
|
176
175
|
}
|
|
177
|
-
export function getScopedRuntimeAccessToken(
|
|
178
|
-
if (!
|
|
176
|
+
export function getScopedRuntimeAccessToken(serverBaseUrl) {
|
|
177
|
+
if (!serverBaseUrl) {
|
|
179
178
|
return undefined;
|
|
180
179
|
}
|
|
181
180
|
try {
|
|
182
|
-
const key = buildRuntimeTokenKey(
|
|
181
|
+
const key = buildRuntimeTokenKey(serverBaseUrl);
|
|
183
182
|
return loadRuntimeTokenStore().get(key);
|
|
184
183
|
}
|
|
185
184
|
catch {
|
|
186
185
|
return undefined;
|
|
187
186
|
}
|
|
188
187
|
}
|
|
189
|
-
export function clearScopedRuntimeAccessToken(
|
|
190
|
-
if (!
|
|
188
|
+
export function clearScopedRuntimeAccessToken(serverBaseUrl) {
|
|
189
|
+
if (!serverBaseUrl) {
|
|
191
190
|
return false;
|
|
192
191
|
}
|
|
193
192
|
try {
|
|
194
|
-
const key = buildRuntimeTokenKey(
|
|
193
|
+
const key = buildRuntimeTokenKey(serverBaseUrl);
|
|
195
194
|
const tokens = loadRuntimeTokenStore();
|
|
196
195
|
const removed = tokens.delete(key);
|
|
197
196
|
if (removed) {
|
|
@@ -256,8 +255,8 @@ export function validateRuntimeBindingToken(token) {
|
|
|
256
255
|
}
|
|
257
256
|
return validateRuntimeTokenStore(candidate);
|
|
258
257
|
}
|
|
259
|
-
export function getRuntimeAccessToken(
|
|
260
|
-
const scopedToken = getScopedRuntimeAccessToken(
|
|
258
|
+
export function getRuntimeAccessToken(serverBaseUrl) {
|
|
259
|
+
const scopedToken = getScopedRuntimeAccessToken(serverBaseUrl);
|
|
261
260
|
if (scopedToken) {
|
|
262
261
|
return scopedToken;
|
|
263
262
|
}
|
|
@@ -306,9 +305,8 @@ export function saveRuntimeBinding(input) {
|
|
|
306
305
|
encoding: "utf8",
|
|
307
306
|
mode: 0o600,
|
|
308
307
|
});
|
|
309
|
-
if (next.
|
|
308
|
+
if (next.schedulerBaseUrl) {
|
|
310
309
|
const key = saveScopedRuntimeAccessToken({
|
|
311
|
-
userId: next.userId,
|
|
312
310
|
serverBaseUrl: next.schedulerBaseUrl,
|
|
313
311
|
accessToken,
|
|
314
312
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-lifecycle-policy.d.ts","sourceRoot":"","sources":["../../src/services/runtime-lifecycle-policy.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,0BAA0B,EAAE,OAAO,CAAC;IACpC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,sBAAsB,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-lifecycle-policy.d.ts","sourceRoot":"","sources":["../../src/services/runtime-lifecycle-policy.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,0BAA0B,EAAE,OAAO,CAAC;IACpC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,sBAAsB,EAAE,OAAO,CAAC;IAChC,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,gBAAgB,EAAE,OAAO,GACxB,oBAAoB,CAWtB;AAED;;;;GAIG;AACH,wBAAgB,uCAAuC,CACrD,gBAAgB,EAAE,OAAO,EACzB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAMT;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAET"}
|
|
@@ -11,6 +11,7 @@ export function buildGracefulShutdownPlan(preserveSessions) {
|
|
|
11
11
|
terminateResidualProcesses: shouldCleanRuntimeState,
|
|
12
12
|
unregisterInstance: shouldCleanRuntimeState,
|
|
13
13
|
clearPersistedSessions: shouldCleanRuntimeState,
|
|
14
|
+
stopTunnelClient: shouldCleanRuntimeState,
|
|
14
15
|
};
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
@@ -20,7 +20,7 @@ import { getRuntimeAccessToken } from "./runtime-binding.js";
|
|
|
20
20
|
export const bridgeEventBuffer = new RingBuffer(2000);
|
|
21
21
|
function runtimeAuthHeaders(options, callbackBaseUrl = resolveRuntimeCallbackBaseUrl(options)) {
|
|
22
22
|
const token = options?.runtimeAccessToken ||
|
|
23
|
-
getRuntimeAccessToken(
|
|
23
|
+
getRuntimeAccessToken(callbackBaseUrl) ||
|
|
24
24
|
getRuntimeAccessToken();
|
|
25
25
|
return token ? { "X-Runtime-Token": token } : undefined;
|
|
26
26
|
}
|