@drisp/cli 0.5.10 → 0.5.11
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 +297 -267
- package/dist/{chunk-RN5AVH3D.js → chunk-2QMUBFZZ.js} +32 -18
- package/dist/cli.js +9 -2
- package/dist/dashboard-daemon.js +1 -1
- 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,144 @@ function instantiateAdapter(sidecar) {
|
|
|
1886
2039
|
return { ok: true, adapter: module.create(parsed.config, sidecar.instanceId) };
|
|
1887
2040
|
}
|
|
1888
2041
|
|
|
1889
|
-
// src/gateway/
|
|
1890
|
-
var
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
this.
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
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
|
|
1959
|
-
});
|
|
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
|
-
}
|
|
1974
|
-
async unregister(id, reason) {
|
|
1975
|
-
const entry = this.entries.get(id);
|
|
1976
|
-
if (!entry) {
|
|
1977
|
-
throw new UnknownChannelError(id);
|
|
1978
|
-
}
|
|
1979
|
-
this.entries.delete(id);
|
|
1980
|
-
entry.abort.abort();
|
|
1981
|
-
await entry.adapter.stop(reason);
|
|
1982
|
-
}
|
|
1983
|
-
async send(channelId, msg) {
|
|
1984
|
-
const entry = this.entries.get(channelId);
|
|
1985
|
-
if (!entry) {
|
|
1986
|
-
throw new UnknownChannelError(channelId);
|
|
2042
|
+
// src/gateway/channelSidecarReconciler.ts
|
|
2043
|
+
var ChannelSidecarReconciler = class {
|
|
2044
|
+
channelManager;
|
|
2045
|
+
home;
|
|
2046
|
+
loadSidecars;
|
|
2047
|
+
instantiateAdapter;
|
|
2048
|
+
stdout;
|
|
2049
|
+
stderr;
|
|
2050
|
+
constructor(opts) {
|
|
2051
|
+
this.channelManager = opts.channelManager;
|
|
2052
|
+
this.home = opts.home;
|
|
2053
|
+
this.loadSidecars = opts.loadSidecars ?? loadChannelSidecars;
|
|
2054
|
+
this.instantiateAdapter = opts.instantiateAdapter ?? instantiateAdapter;
|
|
2055
|
+
this.stdout = opts.stdout ?? ((message) => process.stdout.write(message));
|
|
2056
|
+
this.stderr = opts.stderr ?? ((message) => process.stderr.write(message));
|
|
2057
|
+
}
|
|
2058
|
+
async reconcile(opts) {
|
|
2059
|
+
const results = [];
|
|
2060
|
+
const { sidecars, errors } = this.loadSidecars(this.home);
|
|
2061
|
+
for (const err of errors) {
|
|
2062
|
+
const id = pathIdFromSidecarPath(err.path);
|
|
2063
|
+
results.push({
|
|
2064
|
+
id,
|
|
2065
|
+
ok: false,
|
|
2066
|
+
action: "failed",
|
|
2067
|
+
reason: err.reason
|
|
2068
|
+
});
|
|
2069
|
+
if (opts.logFailures) {
|
|
2070
|
+
this.stderr(`athena-gateway: skipping ${err.path}: ${err.reason}
|
|
2071
|
+
`);
|
|
2072
|
+
}
|
|
1987
2073
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2074
|
+
if (opts.unregisterStale) {
|
|
2075
|
+
const sidecarIds = new Set(sidecars.map((sidecar) => sidecar.instanceId));
|
|
2076
|
+
for (const channel of this.channelManager.listChannels()) {
|
|
2077
|
+
if (sidecarIds.has(channel.id)) continue;
|
|
2078
|
+
try {
|
|
2079
|
+
await this.channelManager.unregister(channel.id, "shutdown");
|
|
2080
|
+
results.push({
|
|
2081
|
+
id: channel.id,
|
|
2082
|
+
ok: true,
|
|
2083
|
+
action: "unregistered"
|
|
2084
|
+
});
|
|
2085
|
+
} catch (err) {
|
|
2086
|
+
const reason = errorReason(err);
|
|
2087
|
+
results.push({
|
|
2088
|
+
id: channel.id,
|
|
2089
|
+
ok: false,
|
|
2090
|
+
action: "failed",
|
|
2091
|
+
reason
|
|
2092
|
+
});
|
|
2093
|
+
if (opts.logFailures) {
|
|
2094
|
+
this.stderr(
|
|
2095
|
+
`athena-gateway: unregister ${channel.id} failed: ${reason}
|
|
2096
|
+
`
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
1994
2101
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2102
|
+
for (const sidecar of sidecars) {
|
|
2103
|
+
const existed = this.channelManager.listChannels().some((channel) => channel.id === sidecar.instanceId);
|
|
2104
|
+
if (existed) {
|
|
2105
|
+
try {
|
|
2106
|
+
await this.channelManager.unregister(sidecar.instanceId, "shutdown");
|
|
2107
|
+
} catch (err) {
|
|
2108
|
+
const reason = errorReason(err);
|
|
2109
|
+
results.push({
|
|
2110
|
+
id: sidecar.instanceId,
|
|
2111
|
+
ok: false,
|
|
2112
|
+
action: "failed",
|
|
2113
|
+
reason
|
|
2114
|
+
});
|
|
2115
|
+
if (opts.logFailures) {
|
|
2116
|
+
this.stderr(
|
|
2117
|
+
`athena-gateway: unregister ${sidecar.instanceId} failed: ${reason}
|
|
2118
|
+
`
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
const built = this.instantiateAdapter(sidecar);
|
|
2125
|
+
if (!built.ok) {
|
|
2126
|
+
results.push({
|
|
2127
|
+
id: sidecar.instanceId,
|
|
2128
|
+
ok: false,
|
|
2129
|
+
action: "failed",
|
|
2130
|
+
reason: built.reason
|
|
2131
|
+
});
|
|
2132
|
+
if (opts.logFailures) {
|
|
2133
|
+
this.stderr(
|
|
2134
|
+
`athena-gateway: ${sidecar.instanceId}: ${built.reason}
|
|
2135
|
+
`
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
continue;
|
|
2139
|
+
}
|
|
2002
2140
|
try {
|
|
2003
|
-
await this.
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
"warn",
|
|
2007
|
-
`channel ${id} stop failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2141
|
+
await this.channelManager.register(
|
|
2142
|
+
built.adapter,
|
|
2143
|
+
sidecar.attachmentId !== void 0 ? { attachmentId: sidecar.attachmentId } : {}
|
|
2008
2144
|
);
|
|
2145
|
+
results.push({
|
|
2146
|
+
id: sidecar.instanceId,
|
|
2147
|
+
ok: true,
|
|
2148
|
+
action: existed ? "replaced" : "registered"
|
|
2149
|
+
});
|
|
2150
|
+
if (opts.logRegistrations) {
|
|
2151
|
+
this.stdout(`athena-gateway: registered ${sidecar.instanceId}
|
|
2152
|
+
`);
|
|
2153
|
+
}
|
|
2154
|
+
} catch (err) {
|
|
2155
|
+
const reason = errorReason(err);
|
|
2156
|
+
results.push({
|
|
2157
|
+
id: sidecar.instanceId,
|
|
2158
|
+
ok: false,
|
|
2159
|
+
action: "failed",
|
|
2160
|
+
reason
|
|
2161
|
+
});
|
|
2162
|
+
if (opts.logFailures) {
|
|
2163
|
+
this.stderr(
|
|
2164
|
+
`athena-gateway: register ${sidecar.instanceId} failed: ${reason}
|
|
2165
|
+
`
|
|
2166
|
+
);
|
|
2167
|
+
}
|
|
2009
2168
|
}
|
|
2010
2169
|
}
|
|
2011
|
-
|
|
2012
|
-
handleInbound(channelId, msg) {
|
|
2013
|
-
if (this.dedupSet.has(msg.idempotencyKey)) {
|
|
2014
|
-
this.log?.("debug", `dropping duplicate inbound ${msg.idempotencyKey}`);
|
|
2015
|
-
return;
|
|
2016
|
-
}
|
|
2017
|
-
this.dedupSet.add(msg.idempotencyKey);
|
|
2018
|
-
this.dedup.push(msg.idempotencyKey);
|
|
2019
|
-
while (this.dedup.length > this.dedupMax) {
|
|
2020
|
-
const evicted = this.dedup.shift();
|
|
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;
|
|
2030
|
-
}
|
|
2031
|
-
try {
|
|
2032
|
-
sink(msg, { attachmentId: this.entries.get(channelId)?.attachmentId });
|
|
2033
|
-
} catch (err) {
|
|
2034
|
-
this.log?.(
|
|
2035
|
-
"warn",
|
|
2036
|
-
`inbound sink threw: ${err instanceof Error ? err.message : String(err)}`
|
|
2037
|
-
);
|
|
2038
|
-
}
|
|
2170
|
+
return { results };
|
|
2039
2171
|
}
|
|
2040
2172
|
};
|
|
2173
|
+
function pathIdFromSidecarPath(filePath) {
|
|
2174
|
+
const base = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
2175
|
+
return base.endsWith(".json") ? base.slice(0, -".json".length) : base;
|
|
2176
|
+
}
|
|
2177
|
+
function errorReason(err) {
|
|
2178
|
+
return err instanceof Error ? err.message : String(err);
|
|
2179
|
+
}
|
|
2041
2180
|
|
|
2042
2181
|
// src/gateway/control/handlers.ts
|
|
2043
2182
|
import { createRequire } from "module";
|
|
@@ -2241,7 +2380,7 @@ var cachedVersion = null;
|
|
|
2241
2380
|
function readVersion() {
|
|
2242
2381
|
if (cachedVersion !== null) return cachedVersion;
|
|
2243
2382
|
try {
|
|
2244
|
-
const injected = "0.5.
|
|
2383
|
+
const injected = "0.5.11";
|
|
2245
2384
|
if (typeof injected === "string" && injected.length > 0) {
|
|
2246
2385
|
cachedVersion = injected;
|
|
2247
2386
|
return cachedVersion;
|
|
@@ -3848,10 +3987,6 @@ function buildListenerStatus(spec, resolvedPort) {
|
|
|
3848
3987
|
loopback: isLoopbackHost(spec.host)
|
|
3849
3988
|
};
|
|
3850
3989
|
}
|
|
3851
|
-
function pathIdFromSidecarPath(filePath) {
|
|
3852
|
-
const base = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
3853
|
-
return base.endsWith(".json") ? base.slice(0, -".json".length) : base;
|
|
3854
|
-
}
|
|
3855
3990
|
async function startDaemon(opts) {
|
|
3856
3991
|
const startedAt = Date.now();
|
|
3857
3992
|
const pid = process.pid;
|
|
@@ -3905,125 +4040,20 @@ async function startDaemon(opts) {
|
|
|
3905
4040
|
channelManager.setInboundSink((inbound, ctx) => {
|
|
3906
4041
|
pipeline.handleInbound(inbound, ctx);
|
|
3907
4042
|
});
|
|
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
|
-
};
|
|
4043
|
+
const channelSidecarReconciler = new ChannelSidecarReconciler({
|
|
4044
|
+
channelManager,
|
|
4045
|
+
home: opts.env?.HOME
|
|
4046
|
+
});
|
|
4047
|
+
const reloadChannels = async () => channelSidecarReconciler.reconcile({
|
|
4048
|
+
unregisterStale: true,
|
|
4049
|
+
logRegistrations: !opts.silent
|
|
4050
|
+
});
|
|
3992
4051
|
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
|
-
}
|
|
4052
|
+
await channelSidecarReconciler.reconcile({
|
|
4053
|
+
unregisterStale: false,
|
|
4054
|
+
logFailures: true,
|
|
4055
|
+
logRegistrations: !opts.silent
|
|
4056
|
+
});
|
|
4027
4057
|
}
|
|
4028
4058
|
const handler = createDispatcher({
|
|
4029
4059
|
startedAt,
|
|
@@ -7167,15 +7167,10 @@ function loadPlugin(pluginDir) {
|
|
|
7167
7167
|
}
|
|
7168
7168
|
const mcpConfigPath = path12.join(pluginDir, ".mcp.json");
|
|
7169
7169
|
const hasMcpConfig = fs14.existsSync(mcpConfigPath);
|
|
7170
|
-
const entries = fs14.readdirSync(skillsDir, { withFileTypes: true });
|
|
7171
7170
|
const commands2 = [];
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
if (!fs14.existsSync(skillPath)) continue;
|
|
7176
|
-
const content = fs14.readFileSync(skillPath, "utf-8");
|
|
7177
|
-
const parsed = parseFrontmatter(content);
|
|
7178
|
-
if (!parsed.frontmatter["user-invocable"]) continue;
|
|
7171
|
+
const loadSkillFile = (skillPath) => {
|
|
7172
|
+
const parsed = parseFrontmatter(fs14.readFileSync(skillPath, "utf-8"));
|
|
7173
|
+
if (!parsed.frontmatter["user-invocable"]) return;
|
|
7179
7174
|
commands2.push(
|
|
7180
7175
|
skillToCommand(
|
|
7181
7176
|
parsed.frontmatter,
|
|
@@ -7183,6 +7178,20 @@ function loadPlugin(pluginDir) {
|
|
|
7183
7178
|
hasMcpConfig ? mcpConfigPath : void 0
|
|
7184
7179
|
)
|
|
7185
7180
|
);
|
|
7181
|
+
};
|
|
7182
|
+
for (const entry of fs14.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
7183
|
+
if (!entry.isDirectory()) continue;
|
|
7184
|
+
const entryDir = path12.join(skillsDir, entry.name);
|
|
7185
|
+
const directSkill = path12.join(entryDir, "SKILL.md");
|
|
7186
|
+
if (fs14.existsSync(directSkill)) {
|
|
7187
|
+
loadSkillFile(directSkill);
|
|
7188
|
+
continue;
|
|
7189
|
+
}
|
|
7190
|
+
for (const nested of fs14.readdirSync(entryDir, { withFileTypes: true })) {
|
|
7191
|
+
if (!nested.isDirectory()) continue;
|
|
7192
|
+
const nestedSkill = path12.join(entryDir, nested.name, "SKILL.md");
|
|
7193
|
+
if (fs14.existsSync(nestedSkill)) loadSkillFile(nestedSkill);
|
|
7194
|
+
}
|
|
7186
7195
|
}
|
|
7187
7196
|
return commands2;
|
|
7188
7197
|
}
|
|
@@ -15030,6 +15039,7 @@ async function runExec(options) {
|
|
|
15030
15039
|
const runtimeFactory = options.runtimeFactory ?? createRuntime;
|
|
15031
15040
|
const sessionStoreFactory = options.sessionStoreFactory ?? createSessionStore;
|
|
15032
15041
|
const athenaSessionId = options.athenaSessionId ?? crypto2.randomUUID();
|
|
15042
|
+
const ownsFeedPublisher = !options.dashboardFeedPublisher;
|
|
15033
15043
|
const dashboardFeedPublisher = options.dashboardFeedPublisher ?? createPairedFeedPublisher();
|
|
15034
15044
|
const dashboardOrigin = options.dashboardOrigin ?? "local";
|
|
15035
15045
|
const output = createExecOutputWriter({
|
|
@@ -15399,6 +15409,9 @@ async function runExec(options) {
|
|
|
15399
15409
|
}
|
|
15400
15410
|
await bridge?.stop();
|
|
15401
15411
|
store.close();
|
|
15412
|
+
if (ownsFeedPublisher) {
|
|
15413
|
+
dashboardFeedPublisher.close();
|
|
15414
|
+
}
|
|
15402
15415
|
}
|
|
15403
15416
|
const resolvedFinalMessage = resolveFinalMessage({
|
|
15404
15417
|
streamMessage: streamFinalMessage,
|
|
@@ -17121,6 +17134,10 @@ function createDashboardPairedExecution(options) {
|
|
|
17121
17134
|
return { kind: "accepted" };
|
|
17122
17135
|
}
|
|
17123
17136
|
return {
|
|
17137
|
+
// `job_assignment` is intentionally not handled here: the runtime daemon
|
|
17138
|
+
// routes assignments through `DashboardAssignmentIntake`, which gates
|
|
17139
|
+
// admission on attachment readiness and then calls `admitAssignment`
|
|
17140
|
+
// directly. `handleFrame` owns only the frames that flow straight through.
|
|
17124
17141
|
handleFrame(frame) {
|
|
17125
17142
|
if (frame.type === "dashboard_decision") {
|
|
17126
17143
|
handleDecision(frame);
|
|
@@ -17130,10 +17147,6 @@ function createDashboardPairedExecution(options) {
|
|
|
17130
17147
|
handleCancel(frame);
|
|
17131
17148
|
return true;
|
|
17132
17149
|
}
|
|
17133
|
-
if (frame.type === "job_assignment") {
|
|
17134
|
-
handleAssignment(frame);
|
|
17135
|
-
return true;
|
|
17136
|
-
}
|
|
17137
17150
|
return false;
|
|
17138
17151
|
},
|
|
17139
17152
|
admitAssignment(frame, input) {
|
|
@@ -17594,15 +17607,16 @@ async function runDashboardRuntimeDaemon(options = {}) {
|
|
|
17594
17607
|
accessToken: token.accessToken
|
|
17595
17608
|
});
|
|
17596
17609
|
} catch (err) {
|
|
17597
|
-
client = null;
|
|
17598
|
-
assignmentIntake.markNotReady();
|
|
17599
17610
|
attachmentReconciler.markStale(token.instanceId);
|
|
17600
|
-
|
|
17601
|
-
|
|
17611
|
+
log(
|
|
17612
|
+
"warn",
|
|
17613
|
+
`runtime daemon: attachment reconciliation failed; continuing with push-only mirror: ${err instanceof Error ? err.message : String(err)}`
|
|
17614
|
+
);
|
|
17602
17615
|
}
|
|
17616
|
+
if (stopped || client !== next) return;
|
|
17617
|
+
assignmentIntake.markReady();
|
|
17603
17618
|
currentInstanceId = token.instanceId;
|
|
17604
17619
|
currentDashboardUrl = config.dashboardUrl;
|
|
17605
|
-
assignmentIntake.markReady();
|
|
17606
17620
|
reconnectAttempt = 0;
|
|
17607
17621
|
scheduleRefresh(token.expiresInSec);
|
|
17608
17622
|
pairedFeedPublisher.attachTransport(next);
|
|
@@ -18062,4 +18076,4 @@ export {
|
|
|
18062
18076
|
startUdsServer,
|
|
18063
18077
|
sendUdsRequest
|
|
18064
18078
|
};
|
|
18065
|
-
//# sourceMappingURL=chunk-
|
|
18079
|
+
//# sourceMappingURL=chunk-2QMUBFZZ.js.map
|
package/dist/cli.js
CHANGED
|
@@ -97,7 +97,7 @@ import {
|
|
|
97
97
|
writeAttachmentMirror,
|
|
98
98
|
writeGatewayClientConfig,
|
|
99
99
|
wsClientOptionsForEndpoint
|
|
100
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-2QMUBFZZ.js";
|
|
101
101
|
import {
|
|
102
102
|
generateId as generateId2
|
|
103
103
|
} from "./chunk-BTKQ67RE.js";
|
|
@@ -1698,6 +1698,13 @@ function useFeed(runtime, messages = [], initialAllowedTools, sessionStore, opti
|
|
|
1698
1698
|
() => options?.dashboardFeedPublisher ?? createPairedFeedPublisher(),
|
|
1699
1699
|
[options?.dashboardFeedPublisher]
|
|
1700
1700
|
);
|
|
1701
|
+
const ownsFeedPublisher = !options?.dashboardFeedPublisher;
|
|
1702
|
+
useEffect(() => {
|
|
1703
|
+
if (!ownsFeedPublisher) return;
|
|
1704
|
+
return () => {
|
|
1705
|
+
dashboardFeedPublisher.close();
|
|
1706
|
+
};
|
|
1707
|
+
}, [ownsFeedPublisher, dashboardFeedPublisher]);
|
|
1701
1708
|
const dashboardDecisionInboxRef = useRef(
|
|
1702
1709
|
options?.dashboardDecisionInbox ?? null
|
|
1703
1710
|
);
|
|
@@ -12663,7 +12670,7 @@ var cachedVersion = null;
|
|
|
12663
12670
|
function readPackageVersion() {
|
|
12664
12671
|
if (cachedVersion !== null) return cachedVersion;
|
|
12665
12672
|
try {
|
|
12666
|
-
const injected = "0.5.
|
|
12673
|
+
const injected = "0.5.11";
|
|
12667
12674
|
if (typeof injected === "string" && injected.length > 0) {
|
|
12668
12675
|
cachedVersion = injected;
|
|
12669
12676
|
return cachedVersion;
|
package/dist/dashboard-daemon.js
CHANGED