@triflux/remote 10.42.0 → 10.44.0
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/cto/hygiene.mjs +1 -1
- package/hub/lib/cto-env.mjs +1 -0
- package/hub/pipe.mjs +9 -0
- package/hub/schema.sql +62 -2
- package/hub/server.mjs +131 -44
- package/hub/store.mjs +616 -19
- package/hub/team/claude-daemon-control.mjs +33 -12
- package/hub/team/cli/commands/start/parse-args.mjs +2 -2
- package/hub/team/conductor.mjs +23 -2
- package/hub/team/execution-mode.mjs +12 -7
- package/hub/team/headless.mjs +311 -24
- package/hub/team/intervention.mjs +530 -0
- package/hub/team/notify.mjs +31 -11
- package/hub/team/retry-state-machine.mjs +12 -3
- package/hub/team/swarm-hypervisor.mjs +21 -2
- package/hub/team/swarm-locks.mjs +17 -1
- package/hub/team/swarm-preflight.mjs +47 -0
- package/hub/team/uds-orchestrator.mjs +62 -9
- package/hub/team/worktree-lifecycle.mjs +56 -59
- package/hub/tools.mjs +16 -3
- package/hub/workers/codex-app-server-worker.mjs +54 -5
- package/hub/workers/codex-mcp.mjs +21 -5
- package/hub/workers/delegator-mcp.mjs +3 -49
- package/package.json +1 -1
- package/scripts/lib/agent-route-policy.mjs +307 -0
- package/scripts/lib/cli-agy.mjs +1 -0
- package/scripts/lib/cli-claude.mjs +1 -0
- package/scripts/lib/cli-codex.mjs +21 -160
- package/scripts/lib/codex-profile-config.mjs +31 -5
- package/scripts/lib/keyword-rules.mjs +12 -0
- package/cto/steward.mjs +0 -362
- package/hub/log-retention.mjs +0 -726
package/cto/hygiene.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "node:fs";
|
|
12
12
|
import { homedir } from "node:os";
|
|
13
13
|
import { basename, dirname, join } from "node:path";
|
|
14
|
-
import { getCtoHygieneApplyMode } from "
|
|
14
|
+
import { getCtoHygieneApplyMode } from "../hub/lib/cto-env.mjs";
|
|
15
15
|
import { appendCtoEvent } from "./events.mjs";
|
|
16
16
|
import { applyHygieneArchiveActions } from "./hygiene-actions.mjs";
|
|
17
17
|
import { resolveLakeRootDir } from "./lake-root.mjs";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@triflux/core/hub/lib/cto-env.mjs";
|
package/hub/pipe.mjs
CHANGED
|
@@ -269,6 +269,15 @@ export function createPipeServer({
|
|
|
269
269
|
payload.heartbeat_ttl_ms || heartbeatTtlMs,
|
|
270
270
|
);
|
|
271
271
|
if (client) touchClient(client);
|
|
272
|
+
if (result?.effective?.updated === false) {
|
|
273
|
+
return {
|
|
274
|
+
ok: false,
|
|
275
|
+
error: {
|
|
276
|
+
code: "AGENT_NOT_FOUND",
|
|
277
|
+
message: `heartbeat target not registered: ${agentId}`,
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
}
|
|
272
281
|
return { ok: true, data: result };
|
|
273
282
|
}
|
|
274
283
|
|
package/hub/schema.sql
CHANGED
|
@@ -11,7 +11,11 @@ CREATE TABLE IF NOT EXISTS agents (
|
|
|
11
11
|
last_seen_ms INTEGER NOT NULL,
|
|
12
12
|
lease_expires_ms INTEGER NOT NULL,
|
|
13
13
|
status TEXT NOT NULL CHECK (status IN ('online','stale','offline')),
|
|
14
|
-
metadata_json TEXT NOT NULL DEFAULT '{}'
|
|
14
|
+
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
15
|
+
project_id TEXT,
|
|
16
|
+
session_id TEXT,
|
|
17
|
+
host_id TEXT,
|
|
18
|
+
transport_locators_json TEXT NOT NULL DEFAULT '[]'
|
|
15
19
|
);
|
|
16
20
|
|
|
17
21
|
-- 메시지 테이블
|
|
@@ -28,7 +32,61 @@ CREATE TABLE IF NOT EXISTS messages (
|
|
|
28
32
|
correlation_id TEXT NOT NULL,
|
|
29
33
|
trace_id TEXT NOT NULL,
|
|
30
34
|
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
31
|
-
status TEXT NOT NULL CHECK (status IN ('queued','delivered','acked','expired','dead_letter'))
|
|
35
|
+
status TEXT NOT NULL CHECK (status IN ('queued','delivered','acked','expired','dead_letter')),
|
|
36
|
+
role_key_wire TEXT
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
-- 동적 역할 레지스트리 (schema v6)
|
|
40
|
+
CREATE TABLE IF NOT EXISTS role_registry (
|
|
41
|
+
role_key_wire TEXT PRIMARY KEY,
|
|
42
|
+
project_id TEXT NOT NULL,
|
|
43
|
+
role_kind TEXT NOT NULL CHECK (role_kind IN ('cto','lead')),
|
|
44
|
+
scope_id TEXT NOT NULL DEFAULT '',
|
|
45
|
+
state TEXT NOT NULL CHECK (
|
|
46
|
+
state IN (
|
|
47
|
+
'electable',
|
|
48
|
+
'elected-probing',
|
|
49
|
+
'active',
|
|
50
|
+
'unreachable',
|
|
51
|
+
'quarantined'
|
|
52
|
+
)
|
|
53
|
+
),
|
|
54
|
+
holder_agent_id TEXT,
|
|
55
|
+
previous_holder_agent_id TEXT,
|
|
56
|
+
epoch INTEGER NOT NULL DEFAULT 0 CHECK (epoch >= 0),
|
|
57
|
+
activation_seq INTEGER NOT NULL DEFAULT 0 CHECK (activation_seq >= 0),
|
|
58
|
+
activation_id TEXT,
|
|
59
|
+
activation_deadline_ms INTEGER,
|
|
60
|
+
holder_lease_expires_ms INTEGER,
|
|
61
|
+
charter_version TEXT,
|
|
62
|
+
charter_acked_epoch INTEGER,
|
|
63
|
+
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
64
|
+
next_probe_ms INTEGER,
|
|
65
|
+
blocked_reason TEXT,
|
|
66
|
+
last_transition_ms INTEGER NOT NULL,
|
|
67
|
+
last_reason TEXT NOT NULL,
|
|
68
|
+
version INTEGER NOT NULL DEFAULT 0,
|
|
69
|
+
CHECK (
|
|
70
|
+
(role_kind = 'cto' AND scope_id = '') OR
|
|
71
|
+
(role_kind = 'lead' AND scope_id <> '')
|
|
72
|
+
),
|
|
73
|
+
UNIQUE(project_id, role_kind, scope_id)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
CREATE TABLE IF NOT EXISTS role_candidates (
|
|
77
|
+
role_key_wire TEXT NOT NULL,
|
|
78
|
+
agent_id TEXT NOT NULL,
|
|
79
|
+
source TEXT NOT NULL CHECK (source IN ('grant','standup','operator')),
|
|
80
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
81
|
+
transport_locators_json TEXT NOT NULL DEFAULT '[]',
|
|
82
|
+
consecutive_probe_failures INTEGER NOT NULL DEFAULT 0,
|
|
83
|
+
excluded_until_ms INTEGER,
|
|
84
|
+
last_probe_ms INTEGER,
|
|
85
|
+
last_probe_code TEXT,
|
|
86
|
+
updated_at_ms INTEGER NOT NULL,
|
|
87
|
+
PRIMARY KEY(role_key_wire, agent_id),
|
|
88
|
+
FOREIGN KEY(role_key_wire)
|
|
89
|
+
REFERENCES role_registry(role_key_wire) ON DELETE CASCADE
|
|
32
90
|
);
|
|
33
91
|
|
|
34
92
|
-- 메시지 수신함 (배달 추적)
|
|
@@ -73,6 +131,7 @@ CREATE INDEX IF NOT EXISTS idx_messages_correlation ON messages(correlation_id);
|
|
|
73
131
|
CREATE INDEX IF NOT EXISTS idx_messages_trace ON messages(trace_id);
|
|
74
132
|
CREATE INDEX IF NOT EXISTS idx_messages_expires ON messages(expires_at_ms);
|
|
75
133
|
CREATE INDEX IF NOT EXISTS idx_messages_priority ON messages(priority DESC, created_at_ms ASC);
|
|
134
|
+
CREATE INDEX IF NOT EXISTS idx_messages_role_key ON messages(role_key_wire, status, expires_at_ms);
|
|
76
135
|
CREATE INDEX IF NOT EXISTS idx_inbox_agent ON message_inbox(agent_id, delivered_at_ms);
|
|
77
136
|
CREATE INDEX IF NOT EXISTS idx_inbox_message ON message_inbox(message_id);
|
|
78
137
|
CREATE INDEX IF NOT EXISTS idx_human_requests_state ON human_requests(state);
|
|
@@ -103,6 +162,7 @@ CREATE TABLE IF NOT EXISTS assign_jobs (
|
|
|
103
162
|
created_at_ms INTEGER NOT NULL,
|
|
104
163
|
updated_at_ms INTEGER NOT NULL,
|
|
105
164
|
started_at_ms INTEGER,
|
|
165
|
+
dispatched_at_ms INTEGER,
|
|
106
166
|
completed_at_ms INTEGER,
|
|
107
167
|
last_retry_at_ms INTEGER
|
|
108
168
|
);
|
package/hub/server.mjs
CHANGED
|
@@ -44,6 +44,13 @@ import {
|
|
|
44
44
|
cleanupStaleFsmonitorDaemons,
|
|
45
45
|
} from "@triflux/core/hub/lib/process-utils.mjs";
|
|
46
46
|
import * as spawnTrace from "@triflux/core/hub/lib/spawn-trace.mjs";
|
|
47
|
+
import {
|
|
48
|
+
clampDurationMs,
|
|
49
|
+
DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
50
|
+
DEFAULT_ASSIGN_TTL_MS,
|
|
51
|
+
DEFAULT_REGISTER_TIMEOUT_SEC,
|
|
52
|
+
resolveWorkerLeaseTtlMs,
|
|
53
|
+
} from "@triflux/core/hub/lib/timeout-defaults.mjs";
|
|
47
54
|
import {
|
|
48
55
|
recordRequest,
|
|
49
56
|
recordWorker,
|
|
@@ -143,7 +150,7 @@ function buildHubUrl(host, port) {
|
|
|
143
150
|
|
|
144
151
|
function parseHubPort(value) {
|
|
145
152
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
146
|
-
return Number.isFinite(parsed) && parsed
|
|
153
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
function isPidAlive(pid, killFn = process.kill) {
|
|
@@ -963,16 +970,23 @@ export async function startHub({
|
|
|
963
970
|
createDelegatorWorker = createDelegatorMcpWorker,
|
|
964
971
|
} = {}) {
|
|
965
972
|
const resolvedPort = parseHubPort(portOpt);
|
|
966
|
-
const portSpecified = Number.isFinite(resolvedPort)
|
|
973
|
+
const portSpecified = Number.isFinite(resolvedPort);
|
|
974
|
+
const ephemeralPort = resolvedPort === 0;
|
|
967
975
|
const livePeer = detectLivePeer();
|
|
968
976
|
const livePidPort = parseHubPort(livePeer?.port);
|
|
969
|
-
|
|
977
|
+
let port = portSpecified
|
|
970
978
|
? resolvedPort
|
|
971
979
|
: resolveHubPort(process.env, {
|
|
972
980
|
detectPeer: () => livePeer,
|
|
973
981
|
});
|
|
974
982
|
|
|
975
|
-
if (
|
|
983
|
+
if (
|
|
984
|
+
portSpecified &&
|
|
985
|
+
!ephemeralPort &&
|
|
986
|
+
livePeer.alive &&
|
|
987
|
+
livePidPort &&
|
|
988
|
+
port !== livePidPort
|
|
989
|
+
) {
|
|
976
990
|
hubLog.warn(
|
|
977
991
|
{
|
|
978
992
|
requestedPort: port,
|
|
@@ -983,12 +997,14 @@ export async function startHub({
|
|
|
983
997
|
);
|
|
984
998
|
}
|
|
985
999
|
|
|
986
|
-
const existingHub =
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1000
|
+
const existingHub = ephemeralPort
|
|
1001
|
+
? null
|
|
1002
|
+
: await tryReuseExistingHub({
|
|
1003
|
+
port,
|
|
1004
|
+
portSpecified,
|
|
1005
|
+
host,
|
|
1006
|
+
detectPeer: () => livePeer,
|
|
1007
|
+
});
|
|
992
1008
|
if (existingHub) return existingHub;
|
|
993
1009
|
|
|
994
1010
|
attachBrokerDiagnostics(brokerInstance);
|
|
@@ -1026,21 +1042,23 @@ export async function startHub({
|
|
|
1026
1042
|
lockHeld = false;
|
|
1027
1043
|
};
|
|
1028
1044
|
|
|
1029
|
-
const lockedExistingHub =
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1045
|
+
const lockedExistingHub = ephemeralPort
|
|
1046
|
+
? null
|
|
1047
|
+
: await tryReuseExistingHub({
|
|
1048
|
+
port,
|
|
1049
|
+
portSpecified,
|
|
1050
|
+
host,
|
|
1051
|
+
detectPeer: () => detectLivePeer(),
|
|
1052
|
+
});
|
|
1035
1053
|
if (lockedExistingHub) {
|
|
1036
1054
|
releaseStartupLock();
|
|
1037
1055
|
return lockedExistingHub;
|
|
1038
1056
|
}
|
|
1039
1057
|
|
|
1040
1058
|
const HUB_TOKEN = process.env.TFX_HUB_TOKEN?.trim() || null;
|
|
1041
|
-
if (HUB_TOKEN) {
|
|
1059
|
+
if (!ephemeralPort && HUB_TOKEN) {
|
|
1042
1060
|
writeOwnedTokenFile({ token: HUB_TOKEN, port });
|
|
1043
|
-
} else {
|
|
1061
|
+
} else if (!ephemeralPort) {
|
|
1044
1062
|
cleanupOwnedTokenFile({ token: HUB_TOKEN, port });
|
|
1045
1063
|
}
|
|
1046
1064
|
|
|
@@ -1540,6 +1558,38 @@ export async function startHub({
|
|
|
1540
1558
|
try {
|
|
1541
1559
|
const body = await parseBody(req);
|
|
1542
1560
|
const { sessionId } = body || {};
|
|
1561
|
+
if (body?.agent_id && body?.cli) {
|
|
1562
|
+
const registered = router.registerAgent({
|
|
1563
|
+
agent_id: body.agent_id,
|
|
1564
|
+
cli: body.cli,
|
|
1565
|
+
capabilities: Array.isArray(body.capabilities)
|
|
1566
|
+
? body.capabilities
|
|
1567
|
+
: [],
|
|
1568
|
+
topics: Array.isArray(body.topics) ? body.topics : [],
|
|
1569
|
+
heartbeat_ttl_ms: body.heartbeat_ttl_ms ?? 300000,
|
|
1570
|
+
metadata:
|
|
1571
|
+
body.metadata && typeof body.metadata === "object"
|
|
1572
|
+
? body.metadata
|
|
1573
|
+
: { source: "session_start" },
|
|
1574
|
+
...(Object.hasOwn(body, "project_id")
|
|
1575
|
+
? { project_id: body.project_id }
|
|
1576
|
+
: {}),
|
|
1577
|
+
...(Object.hasOwn(body, "session_id")
|
|
1578
|
+
? { session_id: body.session_id }
|
|
1579
|
+
: {}),
|
|
1580
|
+
...(Object.hasOwn(body, "host_id")
|
|
1581
|
+
? { host_id: body.host_id }
|
|
1582
|
+
: {}),
|
|
1583
|
+
...(Object.hasOwn(body, "transport_locators_json")
|
|
1584
|
+
? { transport_locators_json: body.transport_locators_json }
|
|
1585
|
+
: {}),
|
|
1586
|
+
});
|
|
1587
|
+
if (!registered?.ok) {
|
|
1588
|
+
throw new Error(
|
|
1589
|
+
registered?.error?.message || "hub agent register failed",
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1543
1593
|
const result = synapseRegistry.register(sessionId, body);
|
|
1544
1594
|
if (!result?.ok) {
|
|
1545
1595
|
throw new Error(result?.reason || "register failed");
|
|
@@ -1690,10 +1740,14 @@ export async function startHub({
|
|
|
1690
1740
|
const {
|
|
1691
1741
|
agent_id,
|
|
1692
1742
|
cli,
|
|
1693
|
-
timeout_sec =
|
|
1743
|
+
timeout_sec = DEFAULT_REGISTER_TIMEOUT_SEC,
|
|
1694
1744
|
topics = [],
|
|
1695
1745
|
capabilities = [],
|
|
1696
1746
|
metadata = {},
|
|
1747
|
+
project_id,
|
|
1748
|
+
session_id,
|
|
1749
|
+
host_id,
|
|
1750
|
+
transport_locators_json,
|
|
1697
1751
|
} = body;
|
|
1698
1752
|
if (!agent_id || !cli) {
|
|
1699
1753
|
return writeJson(res, 400, {
|
|
@@ -1709,7 +1763,12 @@ export async function startHub({
|
|
|
1709
1763
|
metadata?.task ||
|
|
1710
1764
|
cli;
|
|
1711
1765
|
const workerStartedAt = performance.now();
|
|
1712
|
-
const
|
|
1766
|
+
const hasExplicitTimeout = Number.isFinite(
|
|
1767
|
+
Number(body.timeout_sec),
|
|
1768
|
+
);
|
|
1769
|
+
const heartbeat_ttl_ms = hasExplicitTimeout
|
|
1770
|
+
? (timeout_sec + 120) * 1000
|
|
1771
|
+
: Math.max((timeout_sec + 120) * 1000, resolveWorkerLeaseTtlMs());
|
|
1713
1772
|
const result = await pipe.executeCommand("register", {
|
|
1714
1773
|
agent_id,
|
|
1715
1774
|
cli,
|
|
@@ -1717,6 +1776,12 @@ export async function startHub({
|
|
|
1717
1776
|
topics,
|
|
1718
1777
|
heartbeat_ttl_ms,
|
|
1719
1778
|
metadata,
|
|
1779
|
+
...(project_id === undefined ? {} : { project_id }),
|
|
1780
|
+
...(session_id === undefined ? {} : { session_id }),
|
|
1781
|
+
...(host_id === undefined ? {} : { host_id }),
|
|
1782
|
+
...(transport_locators_json === undefined
|
|
1783
|
+
? {}
|
|
1784
|
+
: { transport_locators_json }),
|
|
1720
1785
|
});
|
|
1721
1786
|
if (result.ok) {
|
|
1722
1787
|
workerSpans.set(agent_id, {
|
|
@@ -1725,7 +1790,7 @@ export async function startHub({
|
|
|
1725
1790
|
});
|
|
1726
1791
|
recordWorker(agent_id, "spawned", command, 0);
|
|
1727
1792
|
}
|
|
1728
|
-
return writeJson(res, 200, result);
|
|
1793
|
+
return writeJson(res, result.ok ? 200 : 409, result);
|
|
1729
1794
|
}
|
|
1730
1795
|
|
|
1731
1796
|
if (path === "/bridge/result" && req.method === "POST") {
|
|
@@ -1750,6 +1815,20 @@ export async function startHub({
|
|
|
1750
1815
|
return writeJson(res, 200, result);
|
|
1751
1816
|
}
|
|
1752
1817
|
|
|
1818
|
+
if (path === "/bridge/heartbeat" && req.method === "POST") {
|
|
1819
|
+
const { agent_id, ttl_ms } = body;
|
|
1820
|
+
if (!agent_id)
|
|
1821
|
+
return writeJson(res, 400, { ok: false, error: "agent_id 필수" });
|
|
1822
|
+
const result = await pipe.executeCommand("heartbeat", {
|
|
1823
|
+
agent_id,
|
|
1824
|
+
heartbeat_ttl_ms: clampDurationMs(
|
|
1825
|
+
ttl_ms,
|
|
1826
|
+
resolveWorkerLeaseTtlMs(),
|
|
1827
|
+
),
|
|
1828
|
+
});
|
|
1829
|
+
return writeJson(res, result.ok ? 200 : 404, result);
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1753
1832
|
if (path === "/bridge/control" && req.method === "POST") {
|
|
1754
1833
|
const {
|
|
1755
1834
|
from_agent = "lead",
|
|
@@ -1881,8 +1960,8 @@ export async function startHub({
|
|
|
1881
1960
|
topic = "assign.job",
|
|
1882
1961
|
payload = {},
|
|
1883
1962
|
priority = 5,
|
|
1884
|
-
ttl_ms =
|
|
1885
|
-
timeout_ms =
|
|
1963
|
+
ttl_ms = DEFAULT_ASSIGN_TTL_MS,
|
|
1964
|
+
timeout_ms = DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
1886
1965
|
max_retries = 0,
|
|
1887
1966
|
trace_id,
|
|
1888
1967
|
correlation_id,
|
|
@@ -2352,7 +2431,7 @@ export async function startHub({
|
|
|
2352
2431
|
try {
|
|
2353
2432
|
store.close();
|
|
2354
2433
|
} catch {}
|
|
2355
|
-
if (!preserveTokenFile) {
|
|
2434
|
+
if (!ephemeralPort && !preserveTokenFile) {
|
|
2356
2435
|
cleanupOwnedTokenFile({ token: HUB_TOKEN, port });
|
|
2357
2436
|
}
|
|
2358
2437
|
releaseStartupLock();
|
|
@@ -2426,6 +2505,9 @@ export async function startHub({
|
|
|
2426
2505
|
httpServer.listen(port, host, () => {
|
|
2427
2506
|
httpServer.off("error", onError);
|
|
2428
2507
|
try {
|
|
2508
|
+
const boundAddress = httpServer.address();
|
|
2509
|
+
if (boundAddress && typeof boundAddress === "object")
|
|
2510
|
+
port = boundAddress.port;
|
|
2429
2511
|
let idleTimer = null;
|
|
2430
2512
|
let stopPromise = null;
|
|
2431
2513
|
|
|
@@ -2446,25 +2528,28 @@ export async function startHub({
|
|
|
2446
2528
|
idleTimeoutMs: hubIdleTimeoutMs,
|
|
2447
2529
|
};
|
|
2448
2530
|
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2531
|
+
if (!ephemeralPort) {
|
|
2532
|
+
writeState({
|
|
2533
|
+
pid: process.pid,
|
|
2534
|
+
port,
|
|
2535
|
+
host,
|
|
2536
|
+
auth_mode: HUB_TOKEN ? "token-required" : "localhost-only",
|
|
2537
|
+
url: info.url,
|
|
2538
|
+
pipe_path: pipe.path,
|
|
2539
|
+
pipePath: pipe.path,
|
|
2540
|
+
assign_callback_pipe_path: assignCallbacks.path,
|
|
2541
|
+
assignCallbackPipePath: assignCallbacks.path,
|
|
2542
|
+
authMode: HUB_TOKEN ? "token-required" : "localhost-only",
|
|
2543
|
+
startedAt,
|
|
2544
|
+
started: startedAtMs,
|
|
2545
|
+
version,
|
|
2546
|
+
sessionId,
|
|
2547
|
+
session_id: sessionId,
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2466
2550
|
releaseStartupLock();
|
|
2467
|
-
|
|
2551
|
+
if (!ephemeralPort)
|
|
2552
|
+
void syncHubMcpSettingsIfAvailable({ hubUrl: info.url });
|
|
2468
2553
|
|
|
2469
2554
|
hubLog.info(
|
|
2470
2555
|
{
|
|
@@ -2551,8 +2636,10 @@ export async function startHub({
|
|
|
2551
2636
|
synapseRegistry.destroy();
|
|
2552
2637
|
} catch {}
|
|
2553
2638
|
store.close();
|
|
2554
|
-
|
|
2555
|
-
|
|
2639
|
+
if (!ephemeralPort) {
|
|
2640
|
+
cleanupOwnedPidFile();
|
|
2641
|
+
cleanupOwnedTokenFile({ token: HUB_TOKEN, port });
|
|
2642
|
+
}
|
|
2556
2643
|
httpServer.closeAllConnections();
|
|
2557
2644
|
await new Promise((resolveClose) =>
|
|
2558
2645
|
httpServer.close(resolveClose),
|