@buildautomaton/cli 0.1.10 → 0.1.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/cli.js +146 -67
- package/dist/cli.js.map +4 -4
- package/dist/index.js +146 -67
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22072,6 +22072,26 @@ function applyCliOutboundNetworkPreferences() {
|
|
|
22072
22072
|
|
|
22073
22073
|
// src/bridge/connection/cli-ws-client.ts
|
|
22074
22074
|
import https from "node:https";
|
|
22075
|
+
var CLI_WEBSOCKET_CLIENT_PING_MS = 25e3;
|
|
22076
|
+
function attachWebSocketClientPing(ws, intervalMs) {
|
|
22077
|
+
let timer = null;
|
|
22078
|
+
function clear() {
|
|
22079
|
+
if (timer != null) {
|
|
22080
|
+
clearInterval(timer);
|
|
22081
|
+
timer = null;
|
|
22082
|
+
}
|
|
22083
|
+
}
|
|
22084
|
+
clear();
|
|
22085
|
+
timer = setInterval(() => {
|
|
22086
|
+
if (ws.readyState === wrapper_default.OPEN) {
|
|
22087
|
+
try {
|
|
22088
|
+
ws.ping();
|
|
22089
|
+
} catch {
|
|
22090
|
+
}
|
|
22091
|
+
}
|
|
22092
|
+
}, intervalMs);
|
|
22093
|
+
return clear;
|
|
22094
|
+
}
|
|
22075
22095
|
function buildCliWebSocketClientOptions(wsUrl) {
|
|
22076
22096
|
const wsOptions = { perMessageDeflate: false, family: 4 };
|
|
22077
22097
|
if (wsUrl.startsWith("wss://")) {
|
|
@@ -22100,6 +22120,15 @@ function safeCloseWebSocket(ws) {
|
|
|
22100
22120
|
}
|
|
22101
22121
|
}
|
|
22102
22122
|
}
|
|
22123
|
+
function safeSendWebSocketBinary(ws, data) {
|
|
22124
|
+
if (ws.readyState !== wrapper_default.OPEN) return false;
|
|
22125
|
+
try {
|
|
22126
|
+
ws.send(data, { binary: true });
|
|
22127
|
+
return true;
|
|
22128
|
+
} catch {
|
|
22129
|
+
return false;
|
|
22130
|
+
}
|
|
22131
|
+
}
|
|
22103
22132
|
|
|
22104
22133
|
// src/bridge/connection/create-ws-bridge.ts
|
|
22105
22134
|
var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
|
|
@@ -22108,12 +22137,10 @@ function createWsBridge(options) {
|
|
|
22108
22137
|
const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
|
|
22109
22138
|
applyCliOutboundNetworkPreferences();
|
|
22110
22139
|
const ws = new wrapper_default(url2, buildCliWebSocketClientOptions(url2));
|
|
22111
|
-
let
|
|
22112
|
-
function
|
|
22113
|
-
|
|
22114
|
-
|
|
22115
|
-
clientPingTimer = null;
|
|
22116
|
-
}
|
|
22140
|
+
let clearClientPing = null;
|
|
22141
|
+
function disposeClientPing() {
|
|
22142
|
+
clearClientPing?.();
|
|
22143
|
+
clearClientPing = null;
|
|
22117
22144
|
}
|
|
22118
22145
|
ws.on("unexpected-response", (request, response) => {
|
|
22119
22146
|
const status = response?.statusCode ?? 0;
|
|
@@ -22124,16 +22151,9 @@ function createWsBridge(options) {
|
|
|
22124
22151
|
}
|
|
22125
22152
|
});
|
|
22126
22153
|
ws.on("open", () => {
|
|
22127
|
-
|
|
22154
|
+
disposeClientPing();
|
|
22128
22155
|
if (clientPingIntervalMs != null && clientPingIntervalMs > 0) {
|
|
22129
|
-
|
|
22130
|
-
if (ws.readyState === wrapper_default.OPEN) {
|
|
22131
|
-
try {
|
|
22132
|
-
ws.ping();
|
|
22133
|
-
} catch {
|
|
22134
|
-
}
|
|
22135
|
-
}
|
|
22136
|
-
}, clientPingIntervalMs);
|
|
22156
|
+
clearClientPing = attachWebSocketClientPing(ws, clientPingIntervalMs);
|
|
22137
22157
|
}
|
|
22138
22158
|
onOpen?.();
|
|
22139
22159
|
});
|
|
@@ -22154,11 +22174,11 @@ function createWsBridge(options) {
|
|
|
22154
22174
|
}
|
|
22155
22175
|
});
|
|
22156
22176
|
ws.on("close", (code, reason) => {
|
|
22157
|
-
|
|
22177
|
+
disposeClientPing();
|
|
22158
22178
|
onClose?.(code, reason.toString());
|
|
22159
22179
|
});
|
|
22160
22180
|
ws.on("error", (err) => {
|
|
22161
|
-
|
|
22181
|
+
disposeClientPing();
|
|
22162
22182
|
onError2?.(err);
|
|
22163
22183
|
});
|
|
22164
22184
|
return ws;
|
|
@@ -22844,12 +22864,64 @@ async function createSdkStdioAcpClient(options) {
|
|
|
22844
22864
|
});
|
|
22845
22865
|
}
|
|
22846
22866
|
|
|
22867
|
+
// src/net/transient-local-fetch-retry.ts
|
|
22868
|
+
var LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS = [30, 100, 200, 400];
|
|
22869
|
+
function sleepMs(ms) {
|
|
22870
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
22871
|
+
}
|
|
22872
|
+
function collectErrorText(err) {
|
|
22873
|
+
const parts = [];
|
|
22874
|
+
let e = err;
|
|
22875
|
+
for (let depth = 0; depth < 6 && e != null; depth += 1) {
|
|
22876
|
+
if (e instanceof Error) {
|
|
22877
|
+
parts.push(e.message);
|
|
22878
|
+
e = e.cause;
|
|
22879
|
+
} else {
|
|
22880
|
+
parts.push(String(e));
|
|
22881
|
+
break;
|
|
22882
|
+
}
|
|
22883
|
+
}
|
|
22884
|
+
return parts.join(" ").toLowerCase();
|
|
22885
|
+
}
|
|
22886
|
+
function isTransientLocalServiceError(err) {
|
|
22887
|
+
const text = collectErrorText(err);
|
|
22888
|
+
if (!text) return false;
|
|
22889
|
+
return text.includes("fetch failed") || text.includes("econnrefused") || text.includes("econnreset") || text.includes("epipe") || text.includes("etimedout") || text.includes("socket hang up") || text.includes("network connection lost") || text.includes("ecanceled") || text.includes("aborted") || text.includes("und_err_socket") || text.includes("other side closed") || text.includes("eai_again");
|
|
22890
|
+
}
|
|
22891
|
+
function isIdempotentHttpMethod(method) {
|
|
22892
|
+
const m = method.toUpperCase();
|
|
22893
|
+
return m === "GET" || m === "HEAD" || m === "OPTIONS";
|
|
22894
|
+
}
|
|
22895
|
+
async function fetchWithLocalTransientRetries(url2, init, options) {
|
|
22896
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
22897
|
+
const allowRetry = isIdempotentHttpMethod(method);
|
|
22898
|
+
const delays = options?.delaysMs ?? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS;
|
|
22899
|
+
const maxAttempts = options?.maxAttempts ?? (allowRetry ? Math.max(1, delays.length + 1) : 1);
|
|
22900
|
+
let lastErr;
|
|
22901
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
22902
|
+
try {
|
|
22903
|
+
return await fetch(url2, init);
|
|
22904
|
+
} catch (e) {
|
|
22905
|
+
lastErr = e;
|
|
22906
|
+
const canRetry = allowRetry && attempt < maxAttempts - 1 && isTransientLocalServiceError(e);
|
|
22907
|
+
if (!canRetry) throw e;
|
|
22908
|
+
const waitMs = delays[Math.min(attempt, delays.length - 1)] ?? 100;
|
|
22909
|
+
await sleepMs(waitMs);
|
|
22910
|
+
}
|
|
22911
|
+
}
|
|
22912
|
+
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
22913
|
+
}
|
|
22914
|
+
|
|
22847
22915
|
// src/firehose/proxy/local-proxy.ts
|
|
22848
22916
|
var ALLOWED_HOSTS = ["localhost", "127.0.0.1", "::1"];
|
|
22849
22917
|
function isAllowedHost(host) {
|
|
22850
22918
|
const h = host.replace(/^\[|\]$/g, "");
|
|
22851
22919
|
return ALLOWED_HOSTS.includes(host) || ALLOWED_HOSTS.includes(h);
|
|
22852
22920
|
}
|
|
22921
|
+
function isIdempotentProxyMethod(method) {
|
|
22922
|
+
const m = method.toUpperCase();
|
|
22923
|
+
return m === "GET" || m === "HEAD" || m === "OPTIONS";
|
|
22924
|
+
}
|
|
22853
22925
|
function checkUrlAndHost(request) {
|
|
22854
22926
|
let url2;
|
|
22855
22927
|
try {
|
|
@@ -22877,38 +22949,47 @@ async function proxyToLocal(request) {
|
|
|
22877
22949
|
path: url2.pathname + url2.search,
|
|
22878
22950
|
headers: request.headers
|
|
22879
22951
|
};
|
|
22880
|
-
|
|
22881
|
-
|
|
22882
|
-
|
|
22883
|
-
|
|
22884
|
-
|
|
22885
|
-
|
|
22886
|
-
|
|
22887
|
-
|
|
22888
|
-
|
|
22889
|
-
|
|
22890
|
-
|
|
22952
|
+
const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
|
|
22953
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
22954
|
+
const once = await new Promise((resolve14) => {
|
|
22955
|
+
const req = mod.request(opts, (res) => {
|
|
22956
|
+
const chunks = [];
|
|
22957
|
+
res.on("data", (c) => chunks.push(c));
|
|
22958
|
+
res.on("end", () => {
|
|
22959
|
+
const body = Buffer.concat(chunks).toString("utf8");
|
|
22960
|
+
const headers = {};
|
|
22961
|
+
for (const [k, v] of Object.entries(res.headers)) {
|
|
22962
|
+
if (typeof v === "string") headers[k] = v;
|
|
22963
|
+
else if (Array.isArray(v) && v[0]) headers[k] = v[0];
|
|
22964
|
+
}
|
|
22965
|
+
resolve14({
|
|
22966
|
+
id: request.id,
|
|
22967
|
+
statusCode: res.statusCode ?? 0,
|
|
22968
|
+
headers,
|
|
22969
|
+
body
|
|
22970
|
+
});
|
|
22971
|
+
});
|
|
22972
|
+
});
|
|
22973
|
+
req.on("error", (err) => {
|
|
22891
22974
|
resolve14({
|
|
22892
22975
|
id: request.id,
|
|
22893
|
-
statusCode:
|
|
22894
|
-
headers,
|
|
22895
|
-
body
|
|
22976
|
+
statusCode: 0,
|
|
22977
|
+
headers: {},
|
|
22978
|
+
body: "",
|
|
22979
|
+
error: err.message
|
|
22896
22980
|
});
|
|
22897
22981
|
});
|
|
22982
|
+
const method = request.method.toUpperCase();
|
|
22983
|
+
if (request.body && method !== "GET" && method !== "HEAD") req.write(request.body);
|
|
22984
|
+
req.end();
|
|
22898
22985
|
});
|
|
22899
|
-
|
|
22900
|
-
|
|
22901
|
-
|
|
22902
|
-
|
|
22903
|
-
|
|
22904
|
-
|
|
22905
|
-
|
|
22906
|
-
});
|
|
22907
|
-
});
|
|
22908
|
-
const method = request.method.toUpperCase();
|
|
22909
|
-
if (request.body && method !== "GET" && method !== "HEAD") req.write(request.body);
|
|
22910
|
-
req.end();
|
|
22911
|
-
});
|
|
22986
|
+
const errMsg = once.error ?? "";
|
|
22987
|
+
const canRetry = attempt < maxAttempts - 1 && errMsg !== "" && isTransientLocalServiceError(new Error(errMsg));
|
|
22988
|
+
if (!canRetry) return once;
|
|
22989
|
+
const waitMs = LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS[Math.min(attempt, LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length - 1)] ?? 100;
|
|
22990
|
+
await sleepMs(waitMs);
|
|
22991
|
+
}
|
|
22992
|
+
throw new Error("Local proxy retry loop exited unexpectedly");
|
|
22912
22993
|
}
|
|
22913
22994
|
async function proxyToLocalStreaming(request, callbacks) {
|
|
22914
22995
|
const checked = checkUrlAndHost(request);
|
|
@@ -22925,7 +23006,7 @@ async function proxyToLocalStreaming(request, callbacks) {
|
|
|
22925
23006
|
if (request.body !== void 0 && request.body !== null && method !== "GET" && method !== "HEAD") {
|
|
22926
23007
|
init.body = request.body;
|
|
22927
23008
|
}
|
|
22928
|
-
const res = await
|
|
23009
|
+
const res = await fetchWithLocalTransientRetries(request.url, init);
|
|
22929
23010
|
const headers = {};
|
|
22930
23011
|
res.headers.forEach((value, key) => {
|
|
22931
23012
|
headers[key] = value;
|
|
@@ -31404,6 +31485,9 @@ var DevServerManager = class {
|
|
|
31404
31485
|
function startStreamingProxy(ws, log2, pr) {
|
|
31405
31486
|
proxyToLocalStreaming(pr, {
|
|
31406
31487
|
onStart: (statusCode, headers) => {
|
|
31488
|
+
if (ws.readyState !== wrapper_default.OPEN) {
|
|
31489
|
+
throw new Error("Preview stream interrupted (firehose connection closed)");
|
|
31490
|
+
}
|
|
31407
31491
|
const forwardedHeaders = { ...headers };
|
|
31408
31492
|
const ce = "content-encoding";
|
|
31409
31493
|
const cl = "content-length";
|
|
@@ -31416,7 +31500,10 @@ function startStreamingProxy(ws, log2, pr) {
|
|
|
31416
31500
|
},
|
|
31417
31501
|
onChunk: (chunk) => {
|
|
31418
31502
|
const idBuf = Buffer.from(pr.id, "utf8");
|
|
31419
|
-
|
|
31503
|
+
const buf = Buffer.concat([idBuf, Buffer.from(chunk)]);
|
|
31504
|
+
if (!safeSendWebSocketBinary(ws, buf)) {
|
|
31505
|
+
throw new Error("Preview stream interrupted (firehose connection closed)");
|
|
31506
|
+
}
|
|
31420
31507
|
},
|
|
31421
31508
|
onEnd: () => sendWsMessage(ws, { type: "proxy_result_end", id: pr.id }),
|
|
31422
31509
|
onError: (error40) => {
|
|
@@ -31534,18 +31621,15 @@ function tryConsumeBinaryProxyBody(raw, deps) {
|
|
|
31534
31621
|
}
|
|
31535
31622
|
|
|
31536
31623
|
// src/firehose/connect-firehose.ts
|
|
31537
|
-
var FIREHOSE_CLIENT_PING_MS = 25e3;
|
|
31538
31624
|
function connectFirehose(options) {
|
|
31539
31625
|
const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
|
|
31540
31626
|
const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
|
|
31541
31627
|
applyCliOutboundNetworkPreferences();
|
|
31542
31628
|
const ws = new wrapper_default(wsUrl, buildCliWebSocketClientOptions(wsUrl));
|
|
31543
|
-
let
|
|
31544
|
-
function
|
|
31545
|
-
|
|
31546
|
-
|
|
31547
|
-
clientPingTimer = null;
|
|
31548
|
-
}
|
|
31629
|
+
let clearClientPing = null;
|
|
31630
|
+
function disposeClientPing() {
|
|
31631
|
+
clearClientPing?.();
|
|
31632
|
+
clearClientPing = null;
|
|
31549
31633
|
}
|
|
31550
31634
|
const firehoseSend = (payload) => {
|
|
31551
31635
|
sendWsMessage(ws, payload);
|
|
@@ -31559,15 +31643,8 @@ function connectFirehose(options) {
|
|
|
31559
31643
|
startStreamingProxy: (pr) => startStreamingProxy(ws, log2, pr)
|
|
31560
31644
|
};
|
|
31561
31645
|
ws.on("open", () => {
|
|
31562
|
-
|
|
31563
|
-
|
|
31564
|
-
if (ws.readyState === wrapper_default.OPEN) {
|
|
31565
|
-
try {
|
|
31566
|
-
ws.ping();
|
|
31567
|
-
} catch {
|
|
31568
|
-
}
|
|
31569
|
-
}
|
|
31570
|
-
}, FIREHOSE_CLIENT_PING_MS);
|
|
31646
|
+
disposeClientPing();
|
|
31647
|
+
clearClientPing = attachWebSocketClientPing(ws, CLI_WEBSOCKET_CLIENT_PING_MS);
|
|
31571
31648
|
onOpen?.();
|
|
31572
31649
|
devServerManager.attachFirehose(firehoseSend);
|
|
31573
31650
|
sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
|
|
@@ -31583,18 +31660,21 @@ function connectFirehose(options) {
|
|
|
31583
31660
|
}
|
|
31584
31661
|
});
|
|
31585
31662
|
ws.on("close", (code, reason) => {
|
|
31586
|
-
|
|
31663
|
+
disposeClientPing();
|
|
31587
31664
|
devServerManager.detachFirehose();
|
|
31588
31665
|
const reasonStr = typeof reason === "string" ? reason : reason.toString();
|
|
31589
31666
|
onClose?.(code, reasonStr);
|
|
31590
31667
|
});
|
|
31591
31668
|
ws.on("error", (err) => {
|
|
31592
|
-
|
|
31669
|
+
disposeClientPing();
|
|
31593
31670
|
logCliWebSocketError(log2, "[Proxy and log service]", err);
|
|
31671
|
+
if (ws.readyState === wrapper_default.CONNECTING || ws.readyState === wrapper_default.OPEN) {
|
|
31672
|
+
safeCloseWebSocket(ws);
|
|
31673
|
+
}
|
|
31594
31674
|
});
|
|
31595
31675
|
return {
|
|
31596
31676
|
close() {
|
|
31597
|
-
|
|
31677
|
+
disposeClientPing();
|
|
31598
31678
|
devServerManager.detachFirehose();
|
|
31599
31679
|
safeCloseWebSocket(ws);
|
|
31600
31680
|
},
|
|
@@ -32775,7 +32855,6 @@ async function refreshBridgeTokens(params) {
|
|
|
32775
32855
|
}
|
|
32776
32856
|
|
|
32777
32857
|
// src/bridge/connection/main-bridge-ws-lifecycle.ts
|
|
32778
|
-
var BRIDGE_CLIENT_PING_MS = 25e3;
|
|
32779
32858
|
function createMainBridgeWebSocketLifecycle(params) {
|
|
32780
32859
|
const {
|
|
32781
32860
|
state,
|
|
@@ -32847,7 +32926,7 @@ function createMainBridgeWebSocketLifecycle(params) {
|
|
|
32847
32926
|
const url2 = buildBridgeUrl(apiUrl, workspaceId, tokens.accessToken);
|
|
32848
32927
|
state.currentWs = createWsBridge({
|
|
32849
32928
|
url: url2,
|
|
32850
|
-
clientPingIntervalMs:
|
|
32929
|
+
clientPingIntervalMs: CLI_WEBSOCKET_CLIENT_PING_MS,
|
|
32851
32930
|
onAuthInvalid: () => {
|
|
32852
32931
|
if (authRefreshInFlight) return;
|
|
32853
32932
|
void (async () => {
|