@drisp/cli 0.5.10 → 0.5.13
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/athena-gateway.js +434 -337
- package/dist/{chunk-RN5AVH3D.js → chunk-BUNMENOT.js} +582 -457
- package/dist/cli.js +9 -2
- package/dist/dashboard-daemon.js +1 -1
- package/dist/hook-forwarder.js +5 -5
- package/package.json +1 -1
package/dist/athena-gateway.js
CHANGED
|
@@ -24,6 +24,159 @@ import {
|
|
|
24
24
|
// src/gateway/daemon.ts
|
|
25
25
|
import fs4 from "fs";
|
|
26
26
|
|
|
27
|
+
// src/gateway/channelManager.ts
|
|
28
|
+
var DEFAULT_DEDUP_WINDOW = 1024;
|
|
29
|
+
var DuplicateChannelError = class extends Error {
|
|
30
|
+
constructor(id) {
|
|
31
|
+
super(`channel ${id} already registered`);
|
|
32
|
+
this.name = "DuplicateChannelError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var UnknownChannelError = class extends Error {
|
|
36
|
+
constructor(id) {
|
|
37
|
+
super(`channel ${id} not registered`);
|
|
38
|
+
this.name = "UnknownChannelError";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var ChannelManager = class {
|
|
42
|
+
entries = /* @__PURE__ */ new Map();
|
|
43
|
+
dedup = [];
|
|
44
|
+
dedupSet = /* @__PURE__ */ new Set();
|
|
45
|
+
dedupMax;
|
|
46
|
+
log;
|
|
47
|
+
inboundSink = null;
|
|
48
|
+
healthSink = null;
|
|
49
|
+
stopped = false;
|
|
50
|
+
constructor(opts = {}) {
|
|
51
|
+
this.dedupMax = opts.dedupWindow ?? DEFAULT_DEDUP_WINDOW;
|
|
52
|
+
this.log = opts.log;
|
|
53
|
+
}
|
|
54
|
+
/** Register the single inbound dispatch target. M5 wires the router here. */
|
|
55
|
+
setInboundSink(sink) {
|
|
56
|
+
this.inboundSink = sink;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns the attachmentId associated with `channelId` at registration
|
|
60
|
+
* time, or undefined if the channel is unknown or registered without one.
|
|
61
|
+
*/
|
|
62
|
+
getAttachmentId(channelId) {
|
|
63
|
+
return this.entries.get(channelId)?.attachmentId;
|
|
64
|
+
}
|
|
65
|
+
setHealthSink(sink) {
|
|
66
|
+
this.healthSink = sink;
|
|
67
|
+
}
|
|
68
|
+
listChannels() {
|
|
69
|
+
return [...this.entries.values()].map((e) => ({
|
|
70
|
+
id: e.adapter.id,
|
|
71
|
+
health: e.lastHealth
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
/** Snapshot of currently registered adapters; used by the relay coordinator. */
|
|
75
|
+
listAdapters() {
|
|
76
|
+
return [...this.entries.values()].map((e) => e.adapter);
|
|
77
|
+
}
|
|
78
|
+
async register(adapter, opts = {}) {
|
|
79
|
+
if (this.stopped) {
|
|
80
|
+
throw new Error("channel manager already stopped");
|
|
81
|
+
}
|
|
82
|
+
if (this.entries.has(adapter.id)) {
|
|
83
|
+
throw new DuplicateChannelError(adapter.id);
|
|
84
|
+
}
|
|
85
|
+
const abort = new AbortController();
|
|
86
|
+
const inboundListener = (msg) => this.handleInbound(adapter.id, msg);
|
|
87
|
+
const healthListener = (sample) => {
|
|
88
|
+
const entry2 = this.entries.get(adapter.id);
|
|
89
|
+
if (entry2) entry2.lastHealth = sample;
|
|
90
|
+
this.healthSink?.(sample);
|
|
91
|
+
};
|
|
92
|
+
const startPromise = adapter.start({
|
|
93
|
+
log: (level, msg) => this.log?.(level, `[${adapter.id}] ${msg}`),
|
|
94
|
+
signal: abort.signal,
|
|
95
|
+
emitInbound: inboundListener,
|
|
96
|
+
emitHealth: healthListener
|
|
97
|
+
});
|
|
98
|
+
const entry = {
|
|
99
|
+
adapter,
|
|
100
|
+
abort,
|
|
101
|
+
startPromise
|
|
102
|
+
};
|
|
103
|
+
if (opts.attachmentId !== void 0) entry.attachmentId = opts.attachmentId;
|
|
104
|
+
this.entries.set(adapter.id, entry);
|
|
105
|
+
try {
|
|
106
|
+
await startPromise;
|
|
107
|
+
} catch (err) {
|
|
108
|
+
this.entries.delete(adapter.id);
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async unregister(id, reason) {
|
|
113
|
+
const entry = this.entries.get(id);
|
|
114
|
+
if (!entry) {
|
|
115
|
+
throw new UnknownChannelError(id);
|
|
116
|
+
}
|
|
117
|
+
this.entries.delete(id);
|
|
118
|
+
entry.abort.abort();
|
|
119
|
+
await entry.adapter.stop(reason);
|
|
120
|
+
}
|
|
121
|
+
async send(channelId, msg) {
|
|
122
|
+
const entry = this.entries.get(channelId);
|
|
123
|
+
if (!entry) {
|
|
124
|
+
throw new UnknownChannelError(channelId);
|
|
125
|
+
}
|
|
126
|
+
return entry.adapter.send(msg);
|
|
127
|
+
}
|
|
128
|
+
async probe(channelId) {
|
|
129
|
+
const entry = this.entries.get(channelId);
|
|
130
|
+
if (!entry) {
|
|
131
|
+
throw new UnknownChannelError(channelId);
|
|
132
|
+
}
|
|
133
|
+
return entry.adapter.probe();
|
|
134
|
+
}
|
|
135
|
+
async stop(reason = "shutdown") {
|
|
136
|
+
if (this.stopped) return;
|
|
137
|
+
this.stopped = true;
|
|
138
|
+
const ids = [...this.entries.keys()];
|
|
139
|
+
for (const id of ids.reverse()) {
|
|
140
|
+
try {
|
|
141
|
+
await this.unregister(id, reason);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
this.log?.(
|
|
144
|
+
"warn",
|
|
145
|
+
`channel ${id} stop failed: ${err instanceof Error ? err.message : String(err)}`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
handleInbound(channelId, msg) {
|
|
151
|
+
if (this.dedupSet.has(msg.idempotencyKey)) {
|
|
152
|
+
this.log?.("debug", `dropping duplicate inbound ${msg.idempotencyKey}`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.dedupSet.add(msg.idempotencyKey);
|
|
156
|
+
this.dedup.push(msg.idempotencyKey);
|
|
157
|
+
while (this.dedup.length > this.dedupMax) {
|
|
158
|
+
const evicted = this.dedup.shift();
|
|
159
|
+
if (evicted !== void 0) this.dedupSet.delete(evicted);
|
|
160
|
+
}
|
|
161
|
+
const sink = this.inboundSink;
|
|
162
|
+
if (!sink) {
|
|
163
|
+
this.log?.(
|
|
164
|
+
"debug",
|
|
165
|
+
`no inbound sink registered; dropping ${msg.idempotencyKey}`
|
|
166
|
+
);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
sink(msg, { attachmentId: this.entries.get(channelId)?.attachmentId });
|
|
171
|
+
} catch (err) {
|
|
172
|
+
this.log?.(
|
|
173
|
+
"warn",
|
|
174
|
+
`inbound sink threw: ${err instanceof Error ? err.message : String(err)}`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
27
180
|
// src/infra/config/channels.ts
|
|
28
181
|
import fs from "fs";
|
|
29
182
|
import os from "os";
|
|
@@ -1886,158 +2039,173 @@ function instantiateAdapter(sidecar) {
|
|
|
1886
2039
|
return { ok: true, adapter: module.create(parsed.config, sidecar.instanceId) };
|
|
1887
2040
|
}
|
|
1888
2041
|
|
|
1889
|
-
// src/gateway/
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
constructor(id) {
|
|
1899
|
-
super(`channel ${id} not registered`);
|
|
1900
|
-
this.name = "UnknownChannelError";
|
|
1901
|
-
}
|
|
1902
|
-
};
|
|
1903
|
-
var ChannelManager = class {
|
|
1904
|
-
entries = /* @__PURE__ */ new Map();
|
|
1905
|
-
dedup = [];
|
|
1906
|
-
dedupSet = /* @__PURE__ */ new Set();
|
|
1907
|
-
dedupMax;
|
|
1908
|
-
log;
|
|
1909
|
-
inboundSink = null;
|
|
1910
|
-
healthSink = null;
|
|
1911
|
-
stopped = false;
|
|
1912
|
-
constructor(opts = {}) {
|
|
1913
|
-
this.dedupMax = opts.dedupWindow ?? DEFAULT_DEDUP_WINDOW;
|
|
1914
|
-
this.log = opts.log;
|
|
1915
|
-
}
|
|
1916
|
-
/** Register the single inbound dispatch target. M5 wires the router here. */
|
|
1917
|
-
setInboundSink(sink) {
|
|
1918
|
-
this.inboundSink = sink;
|
|
1919
|
-
}
|
|
1920
|
-
/**
|
|
1921
|
-
* Returns the attachmentId associated with `channelId` at registration
|
|
1922
|
-
* time, or undefined if the channel is unknown or registered without one.
|
|
1923
|
-
*/
|
|
1924
|
-
getAttachmentId(channelId) {
|
|
1925
|
-
return this.entries.get(channelId)?.attachmentId;
|
|
1926
|
-
}
|
|
1927
|
-
setHealthSink(sink) {
|
|
1928
|
-
this.healthSink = sink;
|
|
1929
|
-
}
|
|
1930
|
-
listChannels() {
|
|
1931
|
-
return [...this.entries.values()].map((e) => ({
|
|
1932
|
-
id: e.adapter.id,
|
|
1933
|
-
health: e.lastHealth
|
|
1934
|
-
}));
|
|
1935
|
-
}
|
|
1936
|
-
/** Snapshot of currently registered adapters; used by the relay coordinator. */
|
|
1937
|
-
listAdapters() {
|
|
1938
|
-
return [...this.entries.values()].map((e) => e.adapter);
|
|
1939
|
-
}
|
|
1940
|
-
async register(adapter, opts = {}) {
|
|
1941
|
-
if (this.stopped) {
|
|
1942
|
-
throw new Error("channel manager already stopped");
|
|
1943
|
-
}
|
|
1944
|
-
if (this.entries.has(adapter.id)) {
|
|
1945
|
-
throw new DuplicateChannelError(adapter.id);
|
|
1946
|
-
}
|
|
1947
|
-
const abort = new AbortController();
|
|
1948
|
-
const inboundListener = (msg) => this.handleInbound(adapter.id, msg);
|
|
1949
|
-
const healthListener = (sample) => {
|
|
1950
|
-
const entry2 = this.entries.get(adapter.id);
|
|
1951
|
-
if (entry2) entry2.lastHealth = sample;
|
|
1952
|
-
this.healthSink?.(sample);
|
|
1953
|
-
};
|
|
1954
|
-
const startPromise = adapter.start({
|
|
1955
|
-
log: (level, msg) => this.log?.(level, `[${adapter.id}] ${msg}`),
|
|
1956
|
-
signal: abort.signal,
|
|
1957
|
-
emitInbound: inboundListener,
|
|
1958
|
-
emitHealth: healthListener
|
|
2042
|
+
// src/gateway/channelReconcilePlan.ts
|
|
2043
|
+
function planChannelReconciliation(input) {
|
|
2044
|
+
const actions = [];
|
|
2045
|
+
for (const error2 of input.loadErrors) {
|
|
2046
|
+
actions.push({
|
|
2047
|
+
kind: "load-error",
|
|
2048
|
+
id: pathIdFromSidecarPath(error2.path),
|
|
2049
|
+
path: error2.path,
|
|
2050
|
+
reason: error2.reason
|
|
1959
2051
|
});
|
|
1960
|
-
const entry = {
|
|
1961
|
-
adapter,
|
|
1962
|
-
abort,
|
|
1963
|
-
startPromise
|
|
1964
|
-
};
|
|
1965
|
-
if (opts.attachmentId !== void 0) entry.attachmentId = opts.attachmentId;
|
|
1966
|
-
this.entries.set(adapter.id, entry);
|
|
1967
|
-
try {
|
|
1968
|
-
await startPromise;
|
|
1969
|
-
} catch (err) {
|
|
1970
|
-
this.entries.delete(adapter.id);
|
|
1971
|
-
throw err;
|
|
1972
|
-
}
|
|
1973
2052
|
}
|
|
1974
|
-
|
|
1975
|
-
const
|
|
1976
|
-
|
|
1977
|
-
|
|
2053
|
+
if (input.unregisterStale) {
|
|
2054
|
+
const desiredIds = new Set(
|
|
2055
|
+
input.desired.map((sidecar) => sidecar.instanceId)
|
|
2056
|
+
);
|
|
2057
|
+
for (const id of input.currentChannelIds) {
|
|
2058
|
+
if (desiredIds.has(id)) continue;
|
|
2059
|
+
actions.push({ kind: "unregister-stale", id });
|
|
1978
2060
|
}
|
|
1979
|
-
this.entries.delete(id);
|
|
1980
|
-
entry.abort.abort();
|
|
1981
|
-
await entry.adapter.stop(reason);
|
|
1982
2061
|
}
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
2062
|
+
const currentIds = new Set(input.currentChannelIds);
|
|
2063
|
+
for (const sidecar of input.desired) {
|
|
2064
|
+
actions.push(
|
|
2065
|
+
currentIds.has(sidecar.instanceId) ? { kind: "replace", sidecar } : { kind: "register", sidecar }
|
|
2066
|
+
);
|
|
2067
|
+
}
|
|
2068
|
+
return { actions };
|
|
2069
|
+
}
|
|
2070
|
+
function pathIdFromSidecarPath(filePath) {
|
|
2071
|
+
const base = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
2072
|
+
return base.endsWith(".json") ? base.slice(0, -".json".length) : base;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// src/gateway/channelSidecarReconciler.ts
|
|
2076
|
+
var ChannelSidecarReconciler = class {
|
|
2077
|
+
channelManager;
|
|
2078
|
+
home;
|
|
2079
|
+
loadSidecars;
|
|
2080
|
+
instantiateAdapter;
|
|
2081
|
+
stdout;
|
|
2082
|
+
stderr;
|
|
2083
|
+
constructor(opts) {
|
|
2084
|
+
this.channelManager = opts.channelManager;
|
|
2085
|
+
this.home = opts.home;
|
|
2086
|
+
this.loadSidecars = opts.loadSidecars ?? loadChannelSidecars;
|
|
2087
|
+
this.instantiateAdapter = opts.instantiateAdapter ?? instantiateAdapter;
|
|
2088
|
+
this.stdout = opts.stdout ?? ((message) => process.stdout.write(message));
|
|
2089
|
+
this.stderr = opts.stderr ?? ((message) => process.stderr.write(message));
|
|
2090
|
+
}
|
|
2091
|
+
async reconcile(opts) {
|
|
2092
|
+
const { sidecars, errors } = this.loadSidecars(this.home);
|
|
2093
|
+
const plan = planChannelReconciliation({
|
|
2094
|
+
desired: sidecars,
|
|
2095
|
+
currentChannelIds: this.channelManager.listChannels().map((channel) => channel.id),
|
|
2096
|
+
loadErrors: errors,
|
|
2097
|
+
unregisterStale: opts.unregisterStale
|
|
2098
|
+
});
|
|
2099
|
+
const outcomes = [];
|
|
2100
|
+
for (const action of plan.actions) {
|
|
2101
|
+
outcomes.push(await this.executeAction(action));
|
|
2102
|
+
}
|
|
2103
|
+
for (const outcome of outcomes) {
|
|
2104
|
+
if (!outcome.log) continue;
|
|
2105
|
+
const enabled = outcome.log.stream === "err" ? opts.logFailures : opts.logRegistrations;
|
|
2106
|
+
if (!enabled) continue;
|
|
2107
|
+
const write = outcome.log.stream === "err" ? this.stderr : this.stdout;
|
|
2108
|
+
write(outcome.log.message);
|
|
2109
|
+
}
|
|
2110
|
+
return { results: outcomes.map((outcome) => outcome.result) };
|
|
2111
|
+
}
|
|
2112
|
+
async executeAction(action) {
|
|
2113
|
+
switch (action.kind) {
|
|
2114
|
+
case "load-error":
|
|
2115
|
+
return {
|
|
2116
|
+
result: {
|
|
2117
|
+
id: action.id,
|
|
2118
|
+
ok: false,
|
|
2119
|
+
action: "failed",
|
|
2120
|
+
reason: action.reason
|
|
2121
|
+
},
|
|
2122
|
+
log: {
|
|
2123
|
+
stream: "err",
|
|
2124
|
+
message: `athena-gateway: skipping ${action.path}: ${action.reason}
|
|
2125
|
+
`
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
case "unregister-stale":
|
|
2129
|
+
return this.executeUnregisterStale(action.id);
|
|
2130
|
+
case "replace":
|
|
2131
|
+
return this.executeApply(action.sidecar, true);
|
|
2132
|
+
case "register":
|
|
2133
|
+
return this.executeApply(action.sidecar, false);
|
|
1987
2134
|
}
|
|
1988
|
-
return entry.adapter.send(msg);
|
|
1989
2135
|
}
|
|
1990
|
-
async
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2136
|
+
async executeUnregisterStale(id) {
|
|
2137
|
+
try {
|
|
2138
|
+
await this.channelManager.unregister(id, "shutdown");
|
|
2139
|
+
return { result: { id, ok: true, action: "unregistered" } };
|
|
2140
|
+
} catch (err) {
|
|
2141
|
+
const reason = errorReason(err);
|
|
2142
|
+
return {
|
|
2143
|
+
result: { id, ok: false, action: "failed", reason },
|
|
2144
|
+
log: {
|
|
2145
|
+
stream: "err",
|
|
2146
|
+
message: `athena-gateway: unregister ${id} failed: ${reason}
|
|
2147
|
+
`
|
|
2148
|
+
}
|
|
2149
|
+
};
|
|
1994
2150
|
}
|
|
1995
|
-
return entry.adapter.probe();
|
|
1996
2151
|
}
|
|
1997
|
-
async
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
const ids = [...this.entries.keys()];
|
|
2001
|
-
for (const id of ids.reverse()) {
|
|
2152
|
+
async executeApply(sidecar, existed) {
|
|
2153
|
+
const id = sidecar.instanceId;
|
|
2154
|
+
if (existed) {
|
|
2002
2155
|
try {
|
|
2003
|
-
await this.unregister(id,
|
|
2156
|
+
await this.channelManager.unregister(id, "shutdown");
|
|
2004
2157
|
} catch (err) {
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2158
|
+
const reason = errorReason(err);
|
|
2159
|
+
return {
|
|
2160
|
+
result: { id, ok: false, action: "failed", reason },
|
|
2161
|
+
log: {
|
|
2162
|
+
stream: "err",
|
|
2163
|
+
message: `athena-gateway: unregister ${id} failed: ${reason}
|
|
2164
|
+
`
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2009
2167
|
}
|
|
2010
2168
|
}
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
if (evicted !== void 0) this.dedupSet.delete(evicted);
|
|
2022
|
-
}
|
|
2023
|
-
const sink = this.inboundSink;
|
|
2024
|
-
if (!sink) {
|
|
2025
|
-
this.log?.(
|
|
2026
|
-
"debug",
|
|
2027
|
-
`no inbound sink registered; dropping ${msg.idempotencyKey}`
|
|
2028
|
-
);
|
|
2029
|
-
return;
|
|
2169
|
+
const built = this.instantiateAdapter(sidecar);
|
|
2170
|
+
if (!built.ok) {
|
|
2171
|
+
return {
|
|
2172
|
+
result: { id, ok: false, action: "failed", reason: built.reason },
|
|
2173
|
+
log: {
|
|
2174
|
+
stream: "err",
|
|
2175
|
+
message: `athena-gateway: ${id}: ${built.reason}
|
|
2176
|
+
`
|
|
2177
|
+
}
|
|
2178
|
+
};
|
|
2030
2179
|
}
|
|
2031
2180
|
try {
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
"warn",
|
|
2036
|
-
`inbound sink threw: ${err instanceof Error ? err.message : String(err)}`
|
|
2181
|
+
await this.channelManager.register(
|
|
2182
|
+
built.adapter,
|
|
2183
|
+
sidecar.attachmentId !== void 0 ? { attachmentId: sidecar.attachmentId } : {}
|
|
2037
2184
|
);
|
|
2185
|
+
return {
|
|
2186
|
+
result: { id, ok: true, action: existed ? "replaced" : "registered" },
|
|
2187
|
+
log: {
|
|
2188
|
+
stream: "out",
|
|
2189
|
+
message: `athena-gateway: registered ${id}
|
|
2190
|
+
`
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2193
|
+
} catch (err) {
|
|
2194
|
+
const reason = errorReason(err);
|
|
2195
|
+
return {
|
|
2196
|
+
result: { id, ok: false, action: "failed", reason },
|
|
2197
|
+
log: {
|
|
2198
|
+
stream: "err",
|
|
2199
|
+
message: `athena-gateway: register ${id} failed: ${reason}
|
|
2200
|
+
`
|
|
2201
|
+
}
|
|
2202
|
+
};
|
|
2038
2203
|
}
|
|
2039
2204
|
}
|
|
2040
2205
|
};
|
|
2206
|
+
function errorReason(err) {
|
|
2207
|
+
return err instanceof Error ? err.message : String(err);
|
|
2208
|
+
}
|
|
2041
2209
|
|
|
2042
2210
|
// src/gateway/control/handlers.ts
|
|
2043
2211
|
import { createRequire } from "module";
|
|
@@ -2109,7 +2277,14 @@ var RuntimeBindingStore = class {
|
|
|
2109
2277
|
epoch,
|
|
2110
2278
|
...maybeLastRebindAt(lastRebindAt)
|
|
2111
2279
|
};
|
|
2112
|
-
const
|
|
2280
|
+
const push = input.push ?? existing?.push ?? null;
|
|
2281
|
+
const slot = existing ? { ...existing, runtime, binding: newBinding, push } : {
|
|
2282
|
+
runtime,
|
|
2283
|
+
binding: newBinding,
|
|
2284
|
+
push,
|
|
2285
|
+
staleTimer: null,
|
|
2286
|
+
staleSince: null
|
|
2287
|
+
};
|
|
2113
2288
|
this.clearStaleTimerForSlot(slot);
|
|
2114
2289
|
this.slots.set(key, slot);
|
|
2115
2290
|
if (wasStale && staleSince !== null) {
|
|
@@ -2132,9 +2307,10 @@ var RuntimeBindingStore = class {
|
|
|
2132
2307
|
this.observers.onRuntimeConnectionLost?.({ runtimeId, graceful: true });
|
|
2133
2308
|
}
|
|
2134
2309
|
/**
|
|
2135
|
-
* Called when the transport connection closes.
|
|
2136
|
-
*
|
|
2137
|
-
*
|
|
2310
|
+
* Called when the transport connection closes. Drops the slot's push handle
|
|
2311
|
+
* (immediately deleting the slot when no grace period is configured, otherwise
|
|
2312
|
+
* marking it stale). Returns the runtimeId if the close matched a current
|
|
2313
|
+
* binding; returns null if the connectionId was not recognised.
|
|
2138
2314
|
*/
|
|
2139
2315
|
notifyConnectionClosed(connectionId) {
|
|
2140
2316
|
const entry = this.findSlotByConnectionId(connectionId);
|
|
@@ -2155,6 +2331,7 @@ var RuntimeBindingStore = class {
|
|
|
2155
2331
|
this.observers.onRuntimeConnectionLost?.({ runtimeId, graceful: false });
|
|
2156
2332
|
return runtimeId;
|
|
2157
2333
|
}
|
|
2334
|
+
slot.push = null;
|
|
2158
2335
|
slot.staleSince = now;
|
|
2159
2336
|
slot.staleTimer = setTimeout(() => {
|
|
2160
2337
|
this.expireStaleBinding(key, runtimeId);
|
|
@@ -2189,6 +2366,28 @@ var RuntimeBindingStore = class {
|
|
|
2189
2366
|
const entry = this.findSlotByConnectionId(connectionId);
|
|
2190
2367
|
return entry ? entry.slot.runtime.runtimeId : null;
|
|
2191
2368
|
}
|
|
2369
|
+
/**
|
|
2370
|
+
* Deliver a control-push envelope to the runtime occupying `key`. Returns
|
|
2371
|
+
* false (and pushes nothing) when the slot is empty or its connection has
|
|
2372
|
+
* already been lost.
|
|
2373
|
+
*/
|
|
2374
|
+
pushTo(key, env) {
|
|
2375
|
+
const slot = this.slots.get(key);
|
|
2376
|
+
if (!slot || !slot.push) return false;
|
|
2377
|
+
slot.push(env);
|
|
2378
|
+
return true;
|
|
2379
|
+
}
|
|
2380
|
+
/**
|
|
2381
|
+
* Reportable state for every slot — both attachment-keyed and the legacy
|
|
2382
|
+
* fallback — so status reporting sees all registered runtimes.
|
|
2383
|
+
*/
|
|
2384
|
+
snapshot() {
|
|
2385
|
+
const entries = [];
|
|
2386
|
+
for (const [key, slot] of this.slots) {
|
|
2387
|
+
entries.push({ key, runtime: slot.runtime, binding: slot.binding });
|
|
2388
|
+
}
|
|
2389
|
+
return entries;
|
|
2390
|
+
}
|
|
2192
2391
|
/**
|
|
2193
2392
|
* Returns the attachment slot key (or `undefined` for the legacy slot) that
|
|
2194
2393
|
* holds the given runtime, or `null` if no slot does. Lets callers
|
|
@@ -2241,7 +2440,7 @@ var cachedVersion = null;
|
|
|
2241
2440
|
function readVersion() {
|
|
2242
2441
|
if (cachedVersion !== null) return cachedVersion;
|
|
2243
2442
|
try {
|
|
2244
|
-
const injected = "0.5.
|
|
2443
|
+
const injected = "0.5.13";
|
|
2245
2444
|
if (typeof injected === "string" && injected.length > 0) {
|
|
2246
2445
|
cachedVersion = injected;
|
|
2247
2446
|
return cachedVersion;
|
|
@@ -2516,29 +2715,25 @@ function createDispatcher(deps) {
|
|
|
2516
2715
|
return handle;
|
|
2517
2716
|
}
|
|
2518
2717
|
function runtimeStatusEntries(pipeline) {
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
} : { state: "none" },
|
|
2539
|
-
pendingDispatchCount: pipeline.pendingDispatchCount()
|
|
2540
|
-
}
|
|
2541
|
-
];
|
|
2718
|
+
if (!pipeline) return [];
|
|
2719
|
+
return pipeline.snapshotRuntimes().map(({ runtime, binding }) => ({
|
|
2720
|
+
runtimeId: runtime.runtimeId,
|
|
2721
|
+
defaultAgentId: runtime.defaultAgentId,
|
|
2722
|
+
pid: runtime.pid,
|
|
2723
|
+
registeredAt: runtime.registeredAt,
|
|
2724
|
+
binding: binding?.state === "active" ? {
|
|
2725
|
+
state: "active",
|
|
2726
|
+
boundAt: binding.boundAt,
|
|
2727
|
+
epoch: binding.epoch,
|
|
2728
|
+
...maybeLastRebindAt(binding.lastRebindAt)
|
|
2729
|
+
} : binding?.state === "stale" ? {
|
|
2730
|
+
state: "stale",
|
|
2731
|
+
staleSince: binding.staleSince,
|
|
2732
|
+
epoch: binding.epoch,
|
|
2733
|
+
...maybeLastRebindAt(binding.lastRebindAt)
|
|
2734
|
+
} : { state: "none" },
|
|
2735
|
+
pendingDispatchCount: pipeline.pendingDispatchCountFor(runtime.runtimeId)
|
|
2736
|
+
}));
|
|
2542
2737
|
}
|
|
2543
2738
|
function ok(envelope, ts, payload) {
|
|
2544
2739
|
return { request_id: envelope.request_id, ts, ok: true, payload };
|
|
@@ -2820,13 +3015,6 @@ function deriveSessionKey(loc) {
|
|
|
2820
3015
|
|
|
2821
3016
|
// src/gateway/sessionRegistry.ts
|
|
2822
3017
|
import { randomUUID } from "crypto";
|
|
2823
|
-
var UnknownDispatchError = class extends Error {
|
|
2824
|
-
code = "unknown_dispatch";
|
|
2825
|
-
constructor(id) {
|
|
2826
|
-
super(`unknown dispatchId: ${id}`);
|
|
2827
|
-
this.name = "UnknownDispatchError";
|
|
2828
|
-
}
|
|
2829
|
-
};
|
|
2830
3018
|
var SessionRegistry = class {
|
|
2831
3019
|
dispatches = /* @__PURE__ */ new Map();
|
|
2832
3020
|
idFactory;
|
|
@@ -2841,25 +3029,54 @@ var SessionRegistry = class {
|
|
|
2841
3029
|
dispatchId,
|
|
2842
3030
|
sessionKey: input.sessionKey,
|
|
2843
3031
|
agentId: input.agentId,
|
|
3032
|
+
runtimeId: input.runtimeId,
|
|
3033
|
+
...input.attachmentKey !== void 0 ? { attachmentKey: input.attachmentKey } : {},
|
|
2844
3034
|
location: input.location,
|
|
2845
3035
|
createdAt: this.now()
|
|
2846
3036
|
};
|
|
2847
3037
|
this.dispatches.set(dispatchId, entry);
|
|
2848
3038
|
return entry;
|
|
2849
3039
|
}
|
|
2850
|
-
|
|
3040
|
+
/**
|
|
3041
|
+
* Resolve a parked turn for the runtime claiming to complete it. The entry is
|
|
3042
|
+
* consumed only when the claiming runtime matches the one the turn was
|
|
3043
|
+
* dispatched to — a mismatched runtime cannot cancel or steal another
|
|
3044
|
+
* runtime's turn, and an unknown id is reported rather than thrown.
|
|
3045
|
+
*/
|
|
3046
|
+
completeDispatch(dispatchId, by) {
|
|
2851
3047
|
const entry = this.dispatches.get(dispatchId);
|
|
2852
3048
|
if (!entry) {
|
|
2853
|
-
|
|
3049
|
+
return { kind: "unknown" };
|
|
3050
|
+
}
|
|
3051
|
+
if (entry.runtimeId !== by.runtimeId) {
|
|
3052
|
+
return { kind: "runtime_mismatch", entry };
|
|
2854
3053
|
}
|
|
2855
3054
|
this.dispatches.delete(dispatchId);
|
|
2856
|
-
return entry;
|
|
3055
|
+
return { kind: "completed", entry };
|
|
2857
3056
|
}
|
|
2858
3057
|
pendingDispatchCount() {
|
|
2859
3058
|
return this.dispatches.size;
|
|
2860
3059
|
}
|
|
2861
|
-
|
|
2862
|
-
|
|
3060
|
+
/** Number of parked turns owned by the given runtime. */
|
|
3061
|
+
pendingDispatchCountFor(runtimeId) {
|
|
3062
|
+
let count = 0;
|
|
3063
|
+
for (const entry of this.dispatches.values()) {
|
|
3064
|
+
if (entry.runtimeId === runtimeId) count += 1;
|
|
3065
|
+
}
|
|
3066
|
+
return count;
|
|
3067
|
+
}
|
|
3068
|
+
/**
|
|
3069
|
+
* Remove only the parked turns owned by the given runtime, leaving every other
|
|
3070
|
+
* runtime's in-flight dispatches intact. Used when a single Registered runtime
|
|
3071
|
+
* unregisters or its connection is lost — clearing the correct slot's turns
|
|
3072
|
+
* without a global wipe.
|
|
3073
|
+
*/
|
|
3074
|
+
clearDispatchesFor(runtimeId) {
|
|
3075
|
+
for (const [dispatchId, entry] of this.dispatches) {
|
|
3076
|
+
if (entry.runtimeId === runtimeId) {
|
|
3077
|
+
this.dispatches.delete(dispatchId);
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
2863
3080
|
}
|
|
2864
3081
|
};
|
|
2865
3082
|
|
|
@@ -2986,8 +3203,6 @@ var DispatchPipeline = class {
|
|
|
2986
3203
|
log;
|
|
2987
3204
|
now;
|
|
2988
3205
|
idFactory;
|
|
2989
|
-
pushes = /* @__PURE__ */ new Map();
|
|
2990
|
-
connectionToKey = /* @__PURE__ */ new Map();
|
|
2991
3206
|
constructor(opts) {
|
|
2992
3207
|
this.bindingStore = new RuntimeBindingStore({
|
|
2993
3208
|
gracePeriodMs: opts.gracePeriodMs,
|
|
@@ -3068,6 +3283,8 @@ var DispatchPipeline = class {
|
|
|
3068
3283
|
const entry = this.registry.beginDispatch({
|
|
3069
3284
|
sessionKey,
|
|
3070
3285
|
agentId,
|
|
3286
|
+
runtimeId: current.runtimeId,
|
|
3287
|
+
...key !== void 0 ? { attachmentKey: key } : {},
|
|
3071
3288
|
location: inbound.location
|
|
3072
3289
|
});
|
|
3073
3290
|
this.pushDispatch(key, {
|
|
@@ -3079,9 +3296,7 @@ var DispatchPipeline = class {
|
|
|
3079
3296
|
return { kind: "dispatched", dispatchId: entry.dispatchId, sessionKey };
|
|
3080
3297
|
}
|
|
3081
3298
|
pushDispatch(key, payload) {
|
|
3082
|
-
|
|
3083
|
-
if (!handle) return;
|
|
3084
|
-
handle.push({
|
|
3299
|
+
this.bindingStore.pushTo(key, {
|
|
3085
3300
|
push_id: this.idFactory(),
|
|
3086
3301
|
ts: this.now(),
|
|
3087
3302
|
kind: "session.dispatch.turn",
|
|
@@ -3096,17 +3311,9 @@ var DispatchPipeline = class {
|
|
|
3096
3311
|
defaultAgentId: input.defaultAgentId,
|
|
3097
3312
|
pid: input.pid,
|
|
3098
3313
|
connectionId: input.connectionId,
|
|
3314
|
+
push: input.push,
|
|
3099
3315
|
...input.attachmentId !== void 0 ? { attachmentId: input.attachmentId } : {}
|
|
3100
3316
|
});
|
|
3101
|
-
const previous = this.pushes.get(key);
|
|
3102
|
-
if (previous && previous.connectionId !== input.connectionId) {
|
|
3103
|
-
this.connectionToKey.delete(previous.connectionId);
|
|
3104
|
-
}
|
|
3105
|
-
this.pushes.set(key, {
|
|
3106
|
-
connectionId: input.connectionId,
|
|
3107
|
-
push: input.push
|
|
3108
|
-
});
|
|
3109
|
-
this.connectionToKey.set(input.connectionId, key);
|
|
3110
3317
|
writeGatewayTrace(
|
|
3111
3318
|
`pipeline registered runtime runtimeId=${input.runtimeId} connectionId=${input.connectionId}`
|
|
3112
3319
|
);
|
|
@@ -3114,27 +3321,16 @@ var DispatchPipeline = class {
|
|
|
3114
3321
|
return { registeredAt: result.registeredAt };
|
|
3115
3322
|
}
|
|
3116
3323
|
unregisterRuntime(runtimeId) {
|
|
3117
|
-
const slot = this.findSlotByRuntimeId(runtimeId);
|
|
3118
3324
|
this.bindingStore.unbind(runtimeId);
|
|
3119
|
-
this.registry.
|
|
3120
|
-
if (slot) {
|
|
3121
|
-
const handle = this.pushes.get(slot.key);
|
|
3122
|
-
this.pushes.delete(slot.key);
|
|
3123
|
-
if (handle) this.connectionToKey.delete(handle.connectionId);
|
|
3124
|
-
}
|
|
3325
|
+
this.registry.clearDispatchesFor(runtimeId);
|
|
3125
3326
|
writeGatewayTrace(`pipeline unregistered runtime runtimeId=${runtimeId}`);
|
|
3126
3327
|
}
|
|
3127
3328
|
notifyConnectionClosed(connectionId) {
|
|
3128
|
-
const key = this.connectionToKey.get(connectionId);
|
|
3129
3329
|
const runtimeId = this.bindingStore.notifyConnectionClosed(connectionId);
|
|
3130
3330
|
if (runtimeId === null) return;
|
|
3131
3331
|
writeGatewayTrace(
|
|
3132
3332
|
`pipeline runtime connection closed runtimeId=${runtimeId} connectionId=${connectionId}`
|
|
3133
3333
|
);
|
|
3134
|
-
if (key !== void 0 || this.pushes.has(key)) {
|
|
3135
|
-
this.pushes.delete(key);
|
|
3136
|
-
this.connectionToKey.delete(connectionId);
|
|
3137
|
-
}
|
|
3138
3334
|
}
|
|
3139
3335
|
/**
|
|
3140
3336
|
* Streaming run-event from a registered runtime to its outbound channel
|
|
@@ -3174,32 +3370,35 @@ var DispatchPipeline = class {
|
|
|
3174
3370
|
if (!slot) {
|
|
3175
3371
|
throw new Error("runtime mismatch on session.turn.complete");
|
|
3176
3372
|
}
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3373
|
+
const completion = this.registry.completeDispatch(payload.dispatchId, {
|
|
3374
|
+
runtimeId: payload.runtimeId
|
|
3375
|
+
});
|
|
3376
|
+
if (completion.kind === "unknown") {
|
|
3377
|
+
writeGatewayTrace(
|
|
3378
|
+
`pipeline turn.complete unknown dispatchId=${payload.dispatchId}`
|
|
3379
|
+
);
|
|
3380
|
+
return { delivered: false };
|
|
3381
|
+
}
|
|
3382
|
+
if (completion.kind === "runtime_mismatch") {
|
|
3383
|
+
writeGatewayTrace(
|
|
3384
|
+
`pipeline turn.complete runtime mismatch dispatchId=${payload.dispatchId} authorized=${completion.entry.runtimeId} claimed=${payload.runtimeId}`
|
|
3385
|
+
);
|
|
3386
|
+
return { delivered: false };
|
|
3188
3387
|
}
|
|
3189
|
-
const result = await this.sendOutbound(entry.location, payload);
|
|
3388
|
+
const result = await this.sendOutbound(completion.entry.location, payload);
|
|
3190
3389
|
writeGatewayTrace(
|
|
3191
3390
|
`pipeline sendOutbound delivered dispatchId=${payload.dispatchId} providerMessageId=${result.providerMessageId}`
|
|
3192
3391
|
);
|
|
3193
3392
|
return { delivered: true, providerMessageId: result.providerMessageId };
|
|
3194
3393
|
}
|
|
3195
|
-
async sendOutbound(
|
|
3394
|
+
async sendOutbound(parkedLocation, payload) {
|
|
3196
3395
|
const out = {
|
|
3197
|
-
location:
|
|
3396
|
+
location: parkedLocation,
|
|
3198
3397
|
text: payload.text,
|
|
3199
3398
|
idempotencyKey: payload.idempotencyKey
|
|
3200
3399
|
};
|
|
3201
3400
|
const result = await this.outboundDispatcher.dispatch(
|
|
3202
|
-
|
|
3401
|
+
parkedLocation.channelId,
|
|
3203
3402
|
out
|
|
3204
3403
|
);
|
|
3205
3404
|
if (result.kind === "sent") return result.result;
|
|
@@ -3234,6 +3433,10 @@ var DispatchPipeline = class {
|
|
|
3234
3433
|
getCurrentRuntime() {
|
|
3235
3434
|
return this.bindingStore.getCurrent();
|
|
3236
3435
|
}
|
|
3436
|
+
/** Reportable state for every registered-runtime slot (legacy + attachment-keyed). */
|
|
3437
|
+
snapshotRuntimes() {
|
|
3438
|
+
return this.bindingStore.snapshot();
|
|
3439
|
+
}
|
|
3237
3440
|
getCurrentRuntimeByAttachment(attachmentId) {
|
|
3238
3441
|
return this.bindingStore.getCurrentByAttachment(attachmentId);
|
|
3239
3442
|
}
|
|
@@ -3249,6 +3452,9 @@ var DispatchPipeline = class {
|
|
|
3249
3452
|
pendingDispatchCount() {
|
|
3250
3453
|
return this.registry.pendingDispatchCount();
|
|
3251
3454
|
}
|
|
3455
|
+
pendingDispatchCountFor(runtimeId) {
|
|
3456
|
+
return this.registry.pendingDispatchCountFor(runtimeId);
|
|
3457
|
+
}
|
|
3252
3458
|
pendingInboundCount() {
|
|
3253
3459
|
return this.inboundQueue.size();
|
|
3254
3460
|
}
|
|
@@ -3848,10 +4054,6 @@ function buildListenerStatus(spec, resolvedPort) {
|
|
|
3848
4054
|
loopback: isLoopbackHost(spec.host)
|
|
3849
4055
|
};
|
|
3850
4056
|
}
|
|
3851
|
-
function pathIdFromSidecarPath(filePath) {
|
|
3852
|
-
const base = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
3853
|
-
return base.endsWith(".json") ? base.slice(0, -".json".length) : base;
|
|
3854
|
-
}
|
|
3855
4057
|
async function startDaemon(opts) {
|
|
3856
4058
|
const startedAt = Date.now();
|
|
3857
4059
|
const pid = process.pid;
|
|
@@ -3905,125 +4107,20 @@ async function startDaemon(opts) {
|
|
|
3905
4107
|
channelManager.setInboundSink((inbound, ctx) => {
|
|
3906
4108
|
pipeline.handleInbound(inbound, ctx);
|
|
3907
4109
|
});
|
|
3908
|
-
const
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
ok: false,
|
|
3917
|
-
action: "failed",
|
|
3918
|
-
reason: err.reason
|
|
3919
|
-
});
|
|
3920
|
-
}
|
|
3921
|
-
const sidecarIds = new Set(sidecars.map((s) => s.instanceId));
|
|
3922
|
-
for (const channel of channelManager.listChannels()) {
|
|
3923
|
-
if (sidecarIds.has(channel.id)) continue;
|
|
3924
|
-
try {
|
|
3925
|
-
await channelManager.unregister(channel.id, "shutdown");
|
|
3926
|
-
results.push({
|
|
3927
|
-
id: channel.id,
|
|
3928
|
-
ok: true,
|
|
3929
|
-
action: "unregistered"
|
|
3930
|
-
});
|
|
3931
|
-
} catch (err) {
|
|
3932
|
-
results.push({
|
|
3933
|
-
id: channel.id,
|
|
3934
|
-
ok: false,
|
|
3935
|
-
action: "failed",
|
|
3936
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
3937
|
-
});
|
|
3938
|
-
}
|
|
3939
|
-
}
|
|
3940
|
-
for (const sidecar of sidecars) {
|
|
3941
|
-
const existed = channelManager.listChannels().some((channel) => channel.id === sidecar.instanceId);
|
|
3942
|
-
if (existed) {
|
|
3943
|
-
try {
|
|
3944
|
-
await channelManager.unregister(sidecar.instanceId, "shutdown");
|
|
3945
|
-
} catch (err) {
|
|
3946
|
-
results.push({
|
|
3947
|
-
id: sidecar.instanceId,
|
|
3948
|
-
ok: false,
|
|
3949
|
-
action: "failed",
|
|
3950
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
3951
|
-
});
|
|
3952
|
-
continue;
|
|
3953
|
-
}
|
|
3954
|
-
}
|
|
3955
|
-
const built = instantiateAdapter(sidecar);
|
|
3956
|
-
if (!built.ok) {
|
|
3957
|
-
results.push({
|
|
3958
|
-
id: sidecar.instanceId,
|
|
3959
|
-
ok: false,
|
|
3960
|
-
action: "failed",
|
|
3961
|
-
reason: built.reason
|
|
3962
|
-
});
|
|
3963
|
-
continue;
|
|
3964
|
-
}
|
|
3965
|
-
try {
|
|
3966
|
-
await channelManager.register(
|
|
3967
|
-
built.adapter,
|
|
3968
|
-
sidecar.attachmentId !== void 0 ? { attachmentId: sidecar.attachmentId } : {}
|
|
3969
|
-
);
|
|
3970
|
-
results.push({
|
|
3971
|
-
id: sidecar.instanceId,
|
|
3972
|
-
ok: true,
|
|
3973
|
-
action: existed ? "replaced" : "registered"
|
|
3974
|
-
});
|
|
3975
|
-
if (!opts.silent) {
|
|
3976
|
-
process.stdout.write(
|
|
3977
|
-
`athena-gateway: registered ${sidecar.instanceId}
|
|
3978
|
-
`
|
|
3979
|
-
);
|
|
3980
|
-
}
|
|
3981
|
-
} catch (err) {
|
|
3982
|
-
results.push({
|
|
3983
|
-
id: sidecar.instanceId,
|
|
3984
|
-
ok: false,
|
|
3985
|
-
action: "failed",
|
|
3986
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
3987
|
-
});
|
|
3988
|
-
}
|
|
3989
|
-
}
|
|
3990
|
-
return { results };
|
|
3991
|
-
};
|
|
4110
|
+
const channelSidecarReconciler = new ChannelSidecarReconciler({
|
|
4111
|
+
channelManager,
|
|
4112
|
+
home: opts.env?.HOME
|
|
4113
|
+
});
|
|
4114
|
+
const reloadChannels = async () => channelSidecarReconciler.reconcile({
|
|
4115
|
+
unregisterStale: true,
|
|
4116
|
+
logRegistrations: !opts.silent
|
|
4117
|
+
});
|
|
3992
4118
|
if (!opts.skipChannelLoad) {
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
);
|
|
3999
|
-
}
|
|
4000
|
-
for (const sidecar of sidecars) {
|
|
4001
|
-
const built = instantiateAdapter(sidecar);
|
|
4002
|
-
if (!built.ok) {
|
|
4003
|
-
process.stderr.write(
|
|
4004
|
-
`athena-gateway: ${sidecar.instanceId}: ${built.reason}
|
|
4005
|
-
`
|
|
4006
|
-
);
|
|
4007
|
-
continue;
|
|
4008
|
-
}
|
|
4009
|
-
try {
|
|
4010
|
-
await channelManager.register(
|
|
4011
|
-
built.adapter,
|
|
4012
|
-
sidecar.attachmentId !== void 0 ? { attachmentId: sidecar.attachmentId } : {}
|
|
4013
|
-
);
|
|
4014
|
-
if (!opts.silent) {
|
|
4015
|
-
process.stdout.write(
|
|
4016
|
-
`athena-gateway: registered ${sidecar.instanceId}
|
|
4017
|
-
`
|
|
4018
|
-
);
|
|
4019
|
-
}
|
|
4020
|
-
} catch (err) {
|
|
4021
|
-
process.stderr.write(
|
|
4022
|
-
`athena-gateway: register ${sidecar.instanceId} failed: ${err instanceof Error ? err.message : String(err)}
|
|
4023
|
-
`
|
|
4024
|
-
);
|
|
4025
|
-
}
|
|
4026
|
-
}
|
|
4119
|
+
await channelSidecarReconciler.reconcile({
|
|
4120
|
+
unregisterStale: false,
|
|
4121
|
+
logFailures: true,
|
|
4122
|
+
logRegistrations: !opts.silent
|
|
4123
|
+
});
|
|
4027
4124
|
}
|
|
4028
4125
|
const handler = createDispatcher({
|
|
4029
4126
|
startedAt,
|