@jpcasabiancai/cayenne 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +235 -43
- package/package.json +6 -2
package/dist/cli.js
CHANGED
|
@@ -27,11 +27,11 @@ var CLOUD_SUPABASE_URL, CLOUD_SUPABASE_ANON_KEY, CLOUD_APP_ORIGIN;
|
|
|
27
27
|
var init_cloud = __esm({
|
|
28
28
|
"src/cloud.ts"() {
|
|
29
29
|
"use strict";
|
|
30
|
-
CLOUD_SUPABASE_URL = "";
|
|
30
|
+
CLOUD_SUPABASE_URL = "https://ndqrkqxwkzxgnhkvcunr.supabase.co";
|
|
31
31
|
CLOUD_SUPABASE_ANON_KEY = assertPublishable(
|
|
32
|
-
""
|
|
32
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im5kcXJrcXh3a3p4Z25oa3ZjdW5yIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODg3MTMyNzYsImV4cCI6MjEwNDI4OTI3Nn0.Dlsxw91E1QYZ4eoRDjP80Rf4feY77Z-T-fiIwUDY2k0"
|
|
33
33
|
);
|
|
34
|
-
CLOUD_APP_ORIGIN = "";
|
|
34
|
+
CLOUD_APP_ORIGIN = "https://cayenne-pink.vercel.app";
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
|
|
@@ -1646,11 +1646,13 @@ var init_lock = __esm({
|
|
|
1646
1646
|
});
|
|
1647
1647
|
|
|
1648
1648
|
// ../../packages/db/src/client.ts
|
|
1649
|
-
import {
|
|
1650
|
-
|
|
1649
|
+
import {
|
|
1650
|
+
createClient
|
|
1651
|
+
} from "@supabase/supabase-js";
|
|
1652
|
+
function createRunnerClient(url, anonKey, storage, transport) {
|
|
1651
1653
|
return createClient(url, anonKey, {
|
|
1652
1654
|
auth: { storage, persistSession: true, autoRefreshToken: true, detectSessionInUrl: false },
|
|
1653
|
-
realtime: { params: { eventsPerSecond: 50 } }
|
|
1655
|
+
realtime: { params: { eventsPerSecond: 50 }, transport }
|
|
1654
1656
|
});
|
|
1655
1657
|
}
|
|
1656
1658
|
var init_client = __esm({
|
|
@@ -1667,17 +1669,38 @@ var init_src2 = __esm({
|
|
|
1667
1669
|
}
|
|
1668
1670
|
});
|
|
1669
1671
|
|
|
1672
|
+
// src/realtime-transport.ts
|
|
1673
|
+
import { WebSocket } from "ws";
|
|
1674
|
+
function realtimeTransport(handshakeTimeoutMs = REALTIME_HANDSHAKE_TIMEOUT_MS) {
|
|
1675
|
+
return class RealtimeTransport2 extends WebSocket {
|
|
1676
|
+
static handshakeTimeoutMs = handshakeTimeoutMs;
|
|
1677
|
+
constructor(address, protocols) {
|
|
1678
|
+
super(address, protocols, { handshakeTimeout: RealtimeTransport2.handshakeTimeoutMs });
|
|
1679
|
+
}
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
var REALTIME_HANDSHAKE_TIMEOUT_MS, RealtimeTransport;
|
|
1683
|
+
var init_realtime_transport = __esm({
|
|
1684
|
+
"src/realtime-transport.ts"() {
|
|
1685
|
+
"use strict";
|
|
1686
|
+
REALTIME_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
1687
|
+
RealtimeTransport = realtimeTransport();
|
|
1688
|
+
}
|
|
1689
|
+
});
|
|
1690
|
+
|
|
1670
1691
|
// src/supabase.ts
|
|
1671
1692
|
import { join as join7 } from "path";
|
|
1672
1693
|
function makeClient(cfg, dir = cayenneDir()) {
|
|
1673
1694
|
const storage = new FileSessionStorage(join7(dir, "credentials.json"));
|
|
1674
|
-
|
|
1695
|
+
const transport = RealtimeTransport;
|
|
1696
|
+
return createRunnerClient(cfg.supabaseUrl, cfg.supabaseAnonKey, storage, transport);
|
|
1675
1697
|
}
|
|
1676
1698
|
var init_supabase = __esm({
|
|
1677
1699
|
"src/supabase.ts"() {
|
|
1678
1700
|
"use strict";
|
|
1679
1701
|
init_src2();
|
|
1680
1702
|
init_config();
|
|
1703
|
+
init_realtime_transport();
|
|
1681
1704
|
}
|
|
1682
1705
|
});
|
|
1683
1706
|
|
|
@@ -1978,12 +2001,17 @@ var init_dispatcher = __esm({
|
|
|
1978
2001
|
|
|
1979
2002
|
// src/commands/source.ts
|
|
1980
2003
|
import { LRUCache } from "lru-cache";
|
|
1981
|
-
var STOPPING, byCreatedAt, GIT_READ_KINDS, GIT_WORKTREE_KINDS, laneOf, CommandSource;
|
|
2004
|
+
var STOPPING, REJOIN_MIN_MS, REJOIN_MAX_MS, SOCKET_RESET_MS, LOST, messageOf, byCreatedAt, GIT_READ_KINDS, GIT_WORKTREE_KINDS, laneOf, CommandSource;
|
|
1982
2005
|
var init_source = __esm({
|
|
1983
2006
|
"src/commands/source.ts"() {
|
|
1984
2007
|
"use strict";
|
|
1985
2008
|
init_src();
|
|
1986
2009
|
STOPPING = "runner stopping";
|
|
2010
|
+
REJOIN_MIN_MS = 1e3;
|
|
2011
|
+
REJOIN_MAX_MS = 3e4;
|
|
2012
|
+
SOCKET_RESET_MS = 6e4;
|
|
2013
|
+
LOST = /* @__PURE__ */ new Set(["CHANNEL_ERROR", "TIMED_OUT", "CLOSED"]);
|
|
2014
|
+
messageOf = (err) => err instanceof Error ? err.message : String(err);
|
|
1987
2015
|
byCreatedAt = (a, b) => Date.parse(a.created_at) - Date.parse(b.created_at) || a.id.localeCompare(b.id);
|
|
1988
2016
|
GIT_READ_KINDS = /* @__PURE__ */ new Set(["git.status", "git.diff", "git.branches"]);
|
|
1989
2017
|
GIT_WORKTREE_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -2018,32 +2046,175 @@ var init_source = __esm({
|
|
|
2018
2046
|
/** Rows between `enqueue` and their settlement: an `offer` for one of these registers hooks only. */
|
|
2019
2047
|
inflight = /* @__PURE__ */ new Set();
|
|
2020
2048
|
queues = /* @__PURE__ */ new Map();
|
|
2049
|
+
/** The live `runner:{id}` instance; only its statuses count (see `onStatus`). */
|
|
2021
2050
|
channel = null;
|
|
2051
|
+
/** Every instance this source gave up on: the client can hand one back if its removal did not take. */
|
|
2052
|
+
abandoned = /* @__PURE__ */ new WeakSet();
|
|
2053
|
+
/** Removals of abandoned instances; never rejects. A rejoin waits for it before reusing the topic. */
|
|
2054
|
+
removal = Promise.resolve();
|
|
2055
|
+
rejoinTimer = null;
|
|
2056
|
+
/** Rejoin attempts scheduled since the last `SUBSCRIBED`: the backoff step. */
|
|
2057
|
+
attempts = 0;
|
|
2058
|
+
/** `now()` at the first attempt that found the socket down, or at its last reset; null while up. */
|
|
2059
|
+
socketDownSince = null;
|
|
2022
2060
|
staleMs;
|
|
2023
2061
|
now;
|
|
2024
2062
|
stopped = false;
|
|
2025
2063
|
/** The in-flight `drain()`, so `stop()` can wait for work it never started itself. */
|
|
2026
2064
|
draining = null;
|
|
2065
|
+
/** A `drain()` arrived while one was in flight: that one reads once more before it ends. */
|
|
2066
|
+
again = false;
|
|
2027
2067
|
/** Broadcasts held back while a drain is in flight; see `drain()` for why. */
|
|
2028
2068
|
pending = [];
|
|
2069
|
+
/** Join `runner:{id}` at once; no connectedness check, since `subscribe` connects the socket. */
|
|
2029
2070
|
async start() {
|
|
2030
|
-
this.
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2071
|
+
this.join(this.runnerChannel());
|
|
2072
|
+
}
|
|
2073
|
+
/**
|
|
2074
|
+
* The one place a `runner:{id}` instance is asked for, first join and rejoins alike. Private:
|
|
2075
|
+
* Realtime authorises the join against RLS, which is what keeps this topic this runner's.
|
|
2076
|
+
*/
|
|
2077
|
+
runnerChannel() {
|
|
2078
|
+
return this.db.channel(runnerTopic(this.runnerId), { config: { private: true } });
|
|
2079
|
+
}
|
|
2080
|
+
/** Make `ch` the live instance, then give it the one broadcast and status handler every instance gets. */
|
|
2081
|
+
join(ch) {
|
|
2082
|
+
this.channel = ch;
|
|
2083
|
+
ch.on("broadcast", { event: "command" }, ({ payload }) => this.onBroadcast(payload)).subscribe(
|
|
2084
|
+
(status, err) => this.onStatus(ch, status, err)
|
|
2085
|
+
);
|
|
2086
|
+
}
|
|
2087
|
+
onBroadcast(payload) {
|
|
2088
|
+
const record = payload?.record;
|
|
2089
|
+
const parsed = CommandRow.safeParse(record);
|
|
2090
|
+
if (!parsed.success) {
|
|
2091
|
+
this.log.warn({ issues: parsed.error.issues }, "ignoring malformed command broadcast");
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
if (parsed.data.runner_id !== this.runnerId) {
|
|
2095
|
+
this.log.warn({ id: parsed.data.id }, "ignoring command for another runner");
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2098
|
+
if (this.draining) this.pending.push(parsed.data);
|
|
2099
|
+
else this.enqueue(parsed.data);
|
|
2100
|
+
}
|
|
2101
|
+
/**
|
|
2102
|
+
* The subscribe callback of every instance this source joined (BACKLOG P-29, protocol §8 step
|
|
2103
|
+
* 5). Nothing else rejoins a lost channel: after a three-hour outage the client's own rejoin never
|
|
2104
|
+
* ran, because its socket never reopened. So a lost instance is abandoned and a fresh one joined
|
|
2105
|
+
* after a backoff; every `SUBSCRIBED` drains, since broadcasts sent while the channel was down
|
|
2106
|
+
* are gone and their rows sit `queued`.
|
|
2107
|
+
*
|
|
2108
|
+
* Only the live instance counts. An abandoned one still reports: its own removal fires `CLOSED`
|
|
2109
|
+
* in-line, and handling that as a loss would remove the instance again, whose leave fires
|
|
2110
|
+
* `CLOSED` again, and so on: removals nested inside removals.
|
|
2111
|
+
*/
|
|
2112
|
+
onStatus(ch, status, err) {
|
|
2113
|
+
if (ch !== this.channel) {
|
|
2114
|
+
this.log.debug({ status }, "ignoring the status of a runner channel no longer in use");
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
this.log.info({ status, err: err?.message }, "runner channel status");
|
|
2118
|
+
if (status === "SUBSCRIBED") {
|
|
2119
|
+
this.attempts = 0;
|
|
2120
|
+
this.socketDownSince = null;
|
|
2121
|
+
void this.drain();
|
|
2122
|
+
} else if (LOST.has(status)) {
|
|
2123
|
+
this.abandon(ch);
|
|
2124
|
+
this.scheduleRejoin(status);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
/** Stop listening to `ch` (null first, so its removal's `CLOSED` is ignored), then remove it. */
|
|
2128
|
+
abandon(ch) {
|
|
2129
|
+
if (this.channel === ch) this.channel = null;
|
|
2130
|
+
this.abandoned.add(ch);
|
|
2131
|
+
const left = (async () => this.db.removeChannel(ch))().then(
|
|
2132
|
+
(result) => {
|
|
2133
|
+
if (result === "ok" || result === "timed out") return true;
|
|
2134
|
+
this.log.warn({ result }, "leaving the runner channel failed");
|
|
2135
|
+
return false;
|
|
2136
|
+
},
|
|
2137
|
+
(err) => {
|
|
2138
|
+
this.log.warn({ err: messageOf(err) }, "leaving the runner channel failed");
|
|
2139
|
+
return false;
|
|
2140
|
+
}
|
|
2141
|
+
);
|
|
2142
|
+
this.removal = Promise.all([this.removal, left]).then(() => {
|
|
2143
|
+
});
|
|
2144
|
+
return left;
|
|
2145
|
+
}
|
|
2146
|
+
/**
|
|
2147
|
+
* At most one pending attempt, `min(1 s * 2^n, 30 s)` away, n = attempts since the last
|
|
2148
|
+
* `SUBSCRIBED`. `reason` says why: the lost channel's status, or `SOCKET_DOWN`, `NOT_REMOVED`,
|
|
2149
|
+
* `JOIN_FAILED` for an attempt that could not join. It is logged as `reason`, not `status`, so
|
|
2150
|
+
* a search for the client's statuses never meets one of these.
|
|
2151
|
+
*/
|
|
2152
|
+
scheduleRejoin(reason) {
|
|
2153
|
+
if (this.stopped || this.rejoinTimer) return;
|
|
2154
|
+
const delayMs = Math.min(REJOIN_MIN_MS * 2 ** this.attempts, REJOIN_MAX_MS);
|
|
2155
|
+
this.attempts += 1;
|
|
2156
|
+
this.log.warn({ reason, attempt: this.attempts, delayMs }, "runner channel lost; rejoining");
|
|
2157
|
+
this.rejoinTimer = setTimeout(() => {
|
|
2158
|
+
this.rejoinTimer = null;
|
|
2159
|
+
void this.rejoin();
|
|
2160
|
+
}, delayMs);
|
|
2161
|
+
}
|
|
2162
|
+
/**
|
|
2163
|
+
* One attempt. The client hands back the instance it holds for a topic and drops a topic's
|
|
2164
|
+
* entry when any instance of it closes, so the old removal must have finished before a fresh
|
|
2165
|
+
* instance is created. A socket that is down gets a `connect()` and no join: it buffers every
|
|
2166
|
+
* push while down and flushes them all on reopen, so a join per attempt through a long outage
|
|
2167
|
+
* would arrive as a burst of joins and leaves.
|
|
2168
|
+
*/
|
|
2169
|
+
async rejoin() {
|
|
2170
|
+
await this.removal;
|
|
2171
|
+
if (this.stopped) return;
|
|
2172
|
+
try {
|
|
2173
|
+
if (!this.db.realtime.isConnected()) {
|
|
2174
|
+
await this.reconnectSocket();
|
|
2175
|
+
this.scheduleRejoin("SOCKET_DOWN");
|
|
2035
2176
|
return;
|
|
2036
2177
|
}
|
|
2037
|
-
|
|
2038
|
-
|
|
2178
|
+
this.socketDownSince = null;
|
|
2179
|
+
const ch = this.runnerChannel();
|
|
2180
|
+
if (this.abandoned.has(ch)) {
|
|
2181
|
+
this.log.warn({ attempt: this.attempts }, "runner channel still held after leaving it");
|
|
2182
|
+
this.abandon(ch);
|
|
2183
|
+
this.scheduleRejoin("NOT_REMOVED");
|
|
2039
2184
|
return;
|
|
2040
2185
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2186
|
+
this.join(ch);
|
|
2187
|
+
} catch (err) {
|
|
2188
|
+
this.log.warn(
|
|
2189
|
+
{ attempt: this.attempts, err: messageOf(err) },
|
|
2190
|
+
"runner channel rejoin failed"
|
|
2191
|
+
);
|
|
2192
|
+
if (this.channel) this.abandon(this.channel);
|
|
2193
|
+
this.scheduleRejoin("JOIN_FAILED");
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
/**
|
|
2197
|
+
* Ask a down socket to connect. Once attempts have found it down for `SOCKET_RESET_MS`, tear it
|
|
2198
|
+
* down first: `connect()` returns early while the client still holds a conn, and a conn whose
|
|
2199
|
+
* handshake failed without a close (Node's own WebSocket, BACKLOG P-29) is held forever. The
|
|
2200
|
+
* `ws` transport should never leave one; this clears it whatever the transport. In an ordinary
|
|
2201
|
+
* outage it costs nothing, since phoenix's reconnect loop carries on from this `connect()`.
|
|
2202
|
+
*/
|
|
2203
|
+
async reconnectSocket() {
|
|
2204
|
+
const now = this.now();
|
|
2205
|
+
this.socketDownSince ??= now;
|
|
2206
|
+
const downMs = now - this.socketDownSince;
|
|
2207
|
+
if (downMs >= SOCKET_RESET_MS) {
|
|
2208
|
+
this.log.warn({ downMs }, "realtime socket still down; resetting it");
|
|
2209
|
+
this.socketDownSince = now;
|
|
2210
|
+
try {
|
|
2211
|
+
await this.db.realtime.disconnect();
|
|
2212
|
+
} catch (err) {
|
|
2213
|
+
this.log.warn({ err: messageOf(err) }, "realtime socket reset failed");
|
|
2214
|
+
}
|
|
2215
|
+
if (this.stopped) return;
|
|
2216
|
+
}
|
|
2217
|
+
this.db.realtime.connect();
|
|
2047
2218
|
}
|
|
2048
2219
|
/**
|
|
2049
2220
|
* Pick up rows queued while the runner was offline, oldest first.
|
|
@@ -2051,15 +2222,37 @@ var init_source = __esm({
|
|
|
2051
2222
|
* Broadcasts that land while the select is in flight are buffered and merged in here rather
|
|
2052
2223
|
* than enqueued as they arrive: every drained row predates them, and protocol §2.1 requires
|
|
2053
2224
|
* one session's commands to execute in `created_at` order.
|
|
2225
|
+
*
|
|
2226
|
+
* One drain at a time: a call while one is in flight (a rejoin's `SUBSCRIBED`) asks it for one
|
|
2227
|
+
* more read and waits for it. A second, overlapping select would release the buffer while the
|
|
2228
|
+
* first was still reading older rows. Rows and buffered broadcasts of every read are merged once,
|
|
2229
|
+
* at the end, because a row only the later read finds can predate a broadcast held earlier.
|
|
2054
2230
|
*/
|
|
2055
|
-
|
|
2056
|
-
if (this.stopped) return;
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2231
|
+
drain() {
|
|
2232
|
+
if (this.stopped) return Promise.resolve();
|
|
2233
|
+
if (this.draining) {
|
|
2234
|
+
this.again = true;
|
|
2235
|
+
return this.draining;
|
|
2236
|
+
}
|
|
2237
|
+
this.draining = this.drainAll();
|
|
2238
|
+
return this.draining;
|
|
2239
|
+
}
|
|
2240
|
+
async drainAll() {
|
|
2062
2241
|
const rows = [];
|
|
2242
|
+
try {
|
|
2243
|
+
do {
|
|
2244
|
+
this.again = false;
|
|
2245
|
+
await this.readQueued(rows);
|
|
2246
|
+
} while (this.again && !this.stopped);
|
|
2247
|
+
} finally {
|
|
2248
|
+
const merged = [...rows, ...this.pending].sort(byCreatedAt);
|
|
2249
|
+
this.pending = [];
|
|
2250
|
+
this.draining = null;
|
|
2251
|
+
for (const row of merged) this.enqueue(row);
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
/** One read of this runner's `queued` rows, appended to `rows`. Never rejects. */
|
|
2255
|
+
async readQueued(rows) {
|
|
2063
2256
|
try {
|
|
2064
2257
|
const { data, error } = await this.db.from("runner_commands").select("*").eq("runner_id", this.runnerId).eq("status", "queued").order("created_at", { ascending: true });
|
|
2065
2258
|
if (error) this.log.error({ err: error.message }, "drain failed");
|
|
@@ -2069,17 +2262,16 @@ var init_source = __esm({
|
|
|
2069
2262
|
}
|
|
2070
2263
|
} catch (err) {
|
|
2071
2264
|
this.log.error({ err }, "drain failed");
|
|
2072
|
-
} finally {
|
|
2073
|
-
const merged = [...rows, ...this.pending].sort(byCreatedAt);
|
|
2074
|
-
this.pending = [];
|
|
2075
|
-
this.draining = null;
|
|
2076
|
-
for (const row of merged) this.enqueue(row);
|
|
2077
|
-
release();
|
|
2078
2265
|
}
|
|
2079
2266
|
}
|
|
2080
2267
|
async stop() {
|
|
2081
2268
|
this.stopped = true;
|
|
2082
|
-
if (this.
|
|
2269
|
+
if (this.rejoinTimer) clearTimeout(this.rejoinTimer);
|
|
2270
|
+
this.rejoinTimer = null;
|
|
2271
|
+
const live = this.channel;
|
|
2272
|
+
const left = live ? this.abandon(live) : null;
|
|
2273
|
+
await this.removal;
|
|
2274
|
+
if (left && await left) this.log.info({ status: "CLOSED" }, "runner channel status");
|
|
2083
2275
|
this.pending = [];
|
|
2084
2276
|
await this.draining;
|
|
2085
2277
|
while (this.queues.size > 0) await Promise.allSettled([...this.queues.values()]);
|
|
@@ -8733,7 +8925,7 @@ function createTerminalChannels({
|
|
|
8733
8925
|
log.warn({ terminalId, type: m.type, result }, "terminal frame not sent");
|
|
8734
8926
|
},
|
|
8735
8927
|
(err) => {
|
|
8736
|
-
log.warn({ terminalId, type: m.type, err:
|
|
8928
|
+
log.warn({ terminalId, type: m.type, err: messageOf2(err) }, "terminal frame not sent");
|
|
8737
8929
|
}
|
|
8738
8930
|
).finally(() => {
|
|
8739
8931
|
entry.pending.delete(sent);
|
|
@@ -8773,7 +8965,7 @@ function createTerminalChannels({
|
|
|
8773
8965
|
try {
|
|
8774
8966
|
manager.receive(terminalId, payload, "relay");
|
|
8775
8967
|
} catch (err) {
|
|
8776
|
-
log.warn({ terminalId, err:
|
|
8968
|
+
log.warn({ terminalId, err: messageOf2(err) }, "terminal message refused");
|
|
8777
8969
|
}
|
|
8778
8970
|
});
|
|
8779
8971
|
joined.set(terminalId, { channel, pending: /* @__PURE__ */ new Set() });
|
|
@@ -8804,12 +8996,12 @@ function createTerminalChannels({
|
|
|
8804
8996
|
}
|
|
8805
8997
|
}
|
|
8806
8998
|
}
|
|
8807
|
-
var
|
|
8999
|
+
var messageOf2;
|
|
8808
9000
|
var init_channels = __esm({
|
|
8809
9001
|
"src/terminal/channels.ts"() {
|
|
8810
9002
|
"use strict";
|
|
8811
9003
|
init_src();
|
|
8812
|
-
|
|
9004
|
+
messageOf2 = (err) => err instanceof Error ? err.message : String(err);
|
|
8813
9005
|
}
|
|
8814
9006
|
});
|
|
8815
9007
|
|
|
@@ -9746,7 +9938,7 @@ function createTerminalService({
|
|
|
9746
9938
|
await closeRow(terminalId);
|
|
9747
9939
|
await channels.detach(terminalId);
|
|
9748
9940
|
})().catch((err) => {
|
|
9749
|
-
log.warn({ terminalId, err:
|
|
9941
|
+
log.warn({ terminalId, err: messageOf3(err) }, "terminal exit cleanup failed");
|
|
9750
9942
|
}).finally(() => {
|
|
9751
9943
|
exiting.delete(done);
|
|
9752
9944
|
});
|
|
@@ -9777,7 +9969,7 @@ function createTerminalService({
|
|
|
9777
9969
|
if (!manager.has(terminalId) && !cleanedWhileOpening.has(terminalId)) {
|
|
9778
9970
|
live.delete(terminalId);
|
|
9779
9971
|
await channels.detach(terminalId).catch((detachErr) => {
|
|
9780
|
-
log.warn({ terminalId, err:
|
|
9972
|
+
log.warn({ terminalId, err: messageOf3(detachErr) }, "terminal channel detach failed");
|
|
9781
9973
|
});
|
|
9782
9974
|
await closeRow(terminalId);
|
|
9783
9975
|
}
|
|
@@ -9838,13 +10030,13 @@ function createTerminalService({
|
|
|
9838
10030
|
}
|
|
9839
10031
|
};
|
|
9840
10032
|
}
|
|
9841
|
-
var
|
|
10033
|
+
var messageOf3;
|
|
9842
10034
|
var init_service3 = __esm({
|
|
9843
10035
|
"src/terminal/service.ts"() {
|
|
9844
10036
|
"use strict";
|
|
9845
10037
|
init_manager2();
|
|
9846
10038
|
init_refusals();
|
|
9847
|
-
|
|
10039
|
+
messageOf3 = (err) => err instanceof Error ? err.message : String(err);
|
|
9848
10040
|
}
|
|
9849
10041
|
});
|
|
9850
10042
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jpcasabiancai/cayenne",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist/cli.js"
|
|
6
6
|
],
|
|
@@ -24,7 +24,11 @@
|
|
|
24
24
|
"dev": "tsx src/cli.ts start",
|
|
25
25
|
"build": "tsup",
|
|
26
26
|
"postbuild": "node dist/cli.js --version",
|
|
27
|
-
"
|
|
27
|
+
"release:guard:pre": "tsx src/release/cli.ts --pre",
|
|
28
|
+
"release:guard:post": "tsx src/release/cli.ts --post",
|
|
29
|
+
"release:build": "pnpm release:guard:pre && CAYENNE_REQUIRE_CLOUD=1 pnpm build && pnpm release:guard:post",
|
|
30
|
+
"release:pack": "pnpm release:build && npm pack",
|
|
31
|
+
"prepublishOnly": "pnpm release:build",
|
|
28
32
|
"typecheck": "tsc -p tsconfig.json",
|
|
29
33
|
"test": "vitest run"
|
|
30
34
|
},
|