@buda-ai/connector 0.1.26 → 0.1.28
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 +98 -17
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -523,7 +523,7 @@ import path2 from "path";
|
|
|
523
523
|
// package.json
|
|
524
524
|
var package_default = {
|
|
525
525
|
name: "@buda-ai/connector",
|
|
526
|
-
version: "0.1.
|
|
526
|
+
version: "0.1.28",
|
|
527
527
|
private: false,
|
|
528
528
|
type: "module",
|
|
529
529
|
publishConfig: {
|
|
@@ -550,7 +550,7 @@ var package_default = {
|
|
|
550
550
|
"lint:err": "biome format . --write && biome check . --write --diagnostic-level=error"
|
|
551
551
|
},
|
|
552
552
|
dependencies: {
|
|
553
|
-
"@bunny-agent/daemon": "0.9.
|
|
553
|
+
"@bunny-agent/daemon": "0.9.57",
|
|
554
554
|
"@clack/prompts": "^1.0.0",
|
|
555
555
|
"playwright-core": "^1.52.0",
|
|
556
556
|
ws: "^8.21.0"
|
|
@@ -2215,6 +2215,13 @@ var ConnectorDeletedError = class extends Error {
|
|
|
2215
2215
|
this.name = "ConnectorDeletedError";
|
|
2216
2216
|
}
|
|
2217
2217
|
};
|
|
2218
|
+
var ConnectorUnauthorizedError = class extends Error {
|
|
2219
|
+
constructor(message) {
|
|
2220
|
+
super(message);
|
|
2221
|
+
this.name = "ConnectorUnauthorizedError";
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
var UNREGISTER_TIMEOUT_MS = 3e3;
|
|
2218
2225
|
async function resolveConnectorScope(deviceId, launchConfig) {
|
|
2219
2226
|
const serverUrl = normalizeServerOrigin(getServerUrl(launchConfig));
|
|
2220
2227
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -2301,14 +2308,15 @@ function buildAuthHeaders(config, launchConfig) {
|
|
|
2301
2308
|
if (config.deviceToken) headers["X-Buda-Connector-Device-Token"] = config.deviceToken;
|
|
2302
2309
|
return headers;
|
|
2303
2310
|
}
|
|
2304
|
-
async function postJson(serverUrl, pathname, body, config, launchConfig) {
|
|
2311
|
+
async function postJson(serverUrl, pathname, body, config, launchConfig, options = {}) {
|
|
2305
2312
|
const response = await fetch(`${serverUrl}${pathname}`, {
|
|
2306
2313
|
method: "POST",
|
|
2307
2314
|
headers: {
|
|
2308
2315
|
"Content-Type": "application/json",
|
|
2309
2316
|
...buildAuthHeaders(config, launchConfig)
|
|
2310
2317
|
},
|
|
2311
|
-
body: JSON.stringify(body)
|
|
2318
|
+
body: JSON.stringify(body),
|
|
2319
|
+
...options.timeoutMs ? { signal: AbortSignal.timeout(options.timeoutMs) } : {}
|
|
2312
2320
|
});
|
|
2313
2321
|
if (!response.ok) {
|
|
2314
2322
|
const text2 = await response.text().catch(() => "");
|
|
@@ -2328,6 +2336,9 @@ async function postJson(serverUrl, pathname, body, config, launchConfig) {
|
|
|
2328
2336
|
if (errorCode === "connector_deleted") {
|
|
2329
2337
|
throw new ConnectorDeletedError(errorMessage3 || "Connector no longer exists");
|
|
2330
2338
|
}
|
|
2339
|
+
if (response.status === 401) {
|
|
2340
|
+
throw new ConnectorUnauthorizedError(errorMessage3 || "Connector authentication failed");
|
|
2341
|
+
}
|
|
2331
2342
|
throw new Error(`${pathname} failed: ${response.status} ${errorMessage3}`);
|
|
2332
2343
|
}
|
|
2333
2344
|
return await response.json();
|
|
@@ -2403,7 +2414,8 @@ async function unregisterConnector(config, launchConfig) {
|
|
|
2403
2414
|
connectorId: config.connectorId
|
|
2404
2415
|
},
|
|
2405
2416
|
config,
|
|
2406
|
-
launchConfig
|
|
2417
|
+
launchConfig,
|
|
2418
|
+
{ timeoutMs: UNREGISTER_TIMEOUT_MS }
|
|
2407
2419
|
).catch((error) => {
|
|
2408
2420
|
void writeLog("warn", `unregister failed: ${error instanceof Error ? error.message : error}`);
|
|
2409
2421
|
});
|
|
@@ -2601,6 +2613,7 @@ async function ensureStableConnectorRuntime() {
|
|
|
2601
2613
|
// src/daemon-client.ts
|
|
2602
2614
|
var START_TIMEOUT_MS = 45e3;
|
|
2603
2615
|
var BOOTSTRAP_TIMEOUT_MS = 5 * 6e4;
|
|
2616
|
+
var GRACEFUL_STOP_TIMEOUT_MS = 3e3;
|
|
2604
2617
|
var LOCK_ONLY_RECOVERY_MAX_AGE_MS = 5 * 6e4;
|
|
2605
2618
|
var POLL_INTERVAL_MS = 250;
|
|
2606
2619
|
var wait2 = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
@@ -2754,6 +2767,30 @@ var stopUnresponsiveDaemonProcess = async (pid) => {
|
|
|
2754
2767
|
if (await waitForProcessExit(pid, 3e3)) return;
|
|
2755
2768
|
throw new Error(`Timed-out Connector daemon PID ${pid} could not be stopped safely`);
|
|
2756
2769
|
};
|
|
2770
|
+
var waitForDaemonRelease = async (config, status, configRoot = getConfigRoot()) => {
|
|
2771
|
+
const deadline = Date.now() + GRACEFUL_STOP_TIMEOUT_MS;
|
|
2772
|
+
while (Date.now() < deadline) {
|
|
2773
|
+
if (!isProcessAlive2(status.pid)) return true;
|
|
2774
|
+
const current = await readPersistedDaemonStatus(config, configRoot).catch(() => null);
|
|
2775
|
+
if (!current || current.pid !== status.pid || current.instanceId !== status.instanceId) {
|
|
2776
|
+
return true;
|
|
2777
|
+
}
|
|
2778
|
+
await wait2(50);
|
|
2779
|
+
}
|
|
2780
|
+
return !isProcessAlive2(status.pid);
|
|
2781
|
+
};
|
|
2782
|
+
var stopLiveDaemon = async (config, status, configRoot = getConfigRoot(), command = "stop") => {
|
|
2783
|
+
const stopStatus = await requestDaemonControl(config, command, configRoot);
|
|
2784
|
+
if (command === "stop-if-idle" && !stopStatus.idle) return false;
|
|
2785
|
+
if (await waitForDaemonRelease(config, status, configRoot)) return true;
|
|
2786
|
+
const current = await readPersistedDaemonStatus(config, configRoot).catch(() => null);
|
|
2787
|
+
if (!current || current.pid !== status.pid || current.instanceId !== status.instanceId) {
|
|
2788
|
+
return true;
|
|
2789
|
+
}
|
|
2790
|
+
await stopUnresponsiveDaemonProcess(status.pid);
|
|
2791
|
+
return true;
|
|
2792
|
+
};
|
|
2793
|
+
var shouldRestartUnhealthyDaemonForEnrollment = (status, explicitEnrollment) => explicitEnrollment && status.idle && status.heartbeatHealthy === false;
|
|
2757
2794
|
var readDaemonLockOwnerPid = async (config) => {
|
|
2758
2795
|
const lockPath = `${getConnectorDaemonLockTarget(
|
|
2759
2796
|
config.serverUrl,
|
|
@@ -2817,6 +2854,12 @@ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
|
|
|
2817
2854
|
if (existingConfig) {
|
|
2818
2855
|
const existingStatus = await readLiveStatus(existingConfig);
|
|
2819
2856
|
if (existingStatus) {
|
|
2857
|
+
if (shouldRestartUnhealthyDaemonForEnrollment(
|
|
2858
|
+
existingStatus,
|
|
2859
|
+
Boolean(options.explicitEnrollment)
|
|
2860
|
+
) && await stopLiveDaemon(existingConfig, existingStatus, getConfigRoot(), "stop-if-idle")) {
|
|
2861
|
+
return await startInternalDaemonWithRecovery(launchConfig, mode, void 0, options);
|
|
2862
|
+
}
|
|
2820
2863
|
const preparedRuntime = !shouldReplaceDaemonVersion(existingStatus.version, CONNECTOR_RUNTIME_VERSION) || !existingStatus.idle ? void 0 : await ensureStableConnectorRuntime();
|
|
2821
2864
|
const replaced = await stopOlderIdleDaemon(existingConfig, existingStatus);
|
|
2822
2865
|
if (!replaced) {
|
|
@@ -2937,7 +2980,7 @@ async function stopLocalConnectorDaemon(connectorId, serverUrl, configRoot = get
|
|
|
2937
2980
|
if (!config) throw new Error(`Local Connector ${connectorId} was not found`);
|
|
2938
2981
|
const status = await readLiveStatus(config, configRoot);
|
|
2939
2982
|
if (!status) return;
|
|
2940
|
-
await
|
|
2983
|
+
await stopLiveDaemon(config, status, configRoot);
|
|
2941
2984
|
}
|
|
2942
2985
|
async function stopAllLocalConnectorDaemons(configRoot = getConfigRoot()) {
|
|
2943
2986
|
const configs = await listDeviceConfigs(configRoot);
|
|
@@ -2950,7 +2993,7 @@ ${config.connectorId}`, config);
|
|
|
2950
2993
|
const results = await Promise.allSettled(
|
|
2951
2994
|
[...identities.values()].map(async (config) => {
|
|
2952
2995
|
const status = await readLiveStatus(config, configRoot);
|
|
2953
|
-
if (status) await
|
|
2996
|
+
if (status) await stopLiveDaemon(config, status, configRoot);
|
|
2954
2997
|
})
|
|
2955
2998
|
);
|
|
2956
2999
|
const failures = results.filter((result) => result.status === "rejected");
|
|
@@ -5743,7 +5786,30 @@ import { mkdir as mkdir12 } from "fs/promises";
|
|
|
5743
5786
|
import path15 from "path";
|
|
5744
5787
|
var RECOVERED_DAEMON_STOP_TIMEOUT_MS = 1e4;
|
|
5745
5788
|
var RECOVERED_DAEMON_STOP_POLL_MS = 100;
|
|
5789
|
+
var SHUTDOWN_FORCE_EXIT_MS = 1e4;
|
|
5790
|
+
var SHUTDOWN_STEP_TIMEOUT_MS = 2e3;
|
|
5746
5791
|
var wait3 = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
5792
|
+
var runShutdownStep = async (label, operation) => {
|
|
5793
|
+
let timeout;
|
|
5794
|
+
try {
|
|
5795
|
+
await Promise.race([
|
|
5796
|
+
Promise.resolve().then(operation),
|
|
5797
|
+
new Promise((_, reject) => {
|
|
5798
|
+
timeout = setTimeout(
|
|
5799
|
+
() => reject(new Error(`${label} timed out after ${SHUTDOWN_STEP_TIMEOUT_MS}ms`)),
|
|
5800
|
+
SHUTDOWN_STEP_TIMEOUT_MS
|
|
5801
|
+
);
|
|
5802
|
+
})
|
|
5803
|
+
]);
|
|
5804
|
+
} catch (error) {
|
|
5805
|
+
await writeLog(
|
|
5806
|
+
"warn",
|
|
5807
|
+
`${label} failed during shutdown: ${error instanceof Error ? error.message : String(error)}`
|
|
5808
|
+
).catch(() => void 0);
|
|
5809
|
+
} finally {
|
|
5810
|
+
if (timeout) clearTimeout(timeout);
|
|
5811
|
+
}
|
|
5812
|
+
};
|
|
5747
5813
|
async function stopRecoveredIdleDaemon(config, configRoot) {
|
|
5748
5814
|
const stopStatus = await requestDaemonControl(config, "stop-if-idle", configRoot).catch(
|
|
5749
5815
|
() => null
|
|
@@ -5863,18 +5929,30 @@ async function runConnectorDaemon(launchConfig, options = {}) {
|
|
|
5863
5929
|
const shutdown = async (shutdownOptions = {}) => {
|
|
5864
5930
|
if (shuttingDown) return;
|
|
5865
5931
|
shuttingDown = true;
|
|
5932
|
+
const forceExit = options.exitOnShutdown === false ? void 0 : setTimeout(() => process.exit(0), SHUTDOWN_FORCE_EXIT_MS);
|
|
5866
5933
|
if (interval) clearInterval(interval);
|
|
5867
5934
|
relay.stop();
|
|
5868
5935
|
try {
|
|
5869
|
-
await
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5936
|
+
await Promise.all([
|
|
5937
|
+
runShutdownStep("browser session cleanup", closeAllBrowserSessions),
|
|
5938
|
+
...shutdownOptions.unregister === false ? [] : [
|
|
5939
|
+
runShutdownStep(
|
|
5940
|
+
"connector unregister",
|
|
5941
|
+
() => unregisterConnector(config, launchConfig)
|
|
5942
|
+
)
|
|
5943
|
+
],
|
|
5944
|
+
runShutdownStep(
|
|
5945
|
+
"embedded Bunny daemon shutdown",
|
|
5946
|
+
() => stopEmbeddedBunnyDaemon(activeBunnyDaemon)
|
|
5947
|
+
)
|
|
5948
|
+
]);
|
|
5874
5949
|
} finally {
|
|
5875
|
-
await
|
|
5876
|
-
|
|
5877
|
-
|
|
5950
|
+
await Promise.all([
|
|
5951
|
+
runShutdownStep("control channel shutdown", async () => control?.close()),
|
|
5952
|
+
runShutdownStep("daemon lock release", () => daemonLock.release()),
|
|
5953
|
+
runShutdownStep("console capture restore", async () => restoreConsole?.())
|
|
5954
|
+
]);
|
|
5955
|
+
if (forceExit) clearTimeout(forceExit);
|
|
5878
5956
|
if (options.exitOnShutdown !== false) process.exit(0);
|
|
5879
5957
|
}
|
|
5880
5958
|
};
|
|
@@ -5889,8 +5967,11 @@ async function runConnectorDaemon(launchConfig, options = {}) {
|
|
|
5889
5967
|
});
|
|
5890
5968
|
}).catch((error) => {
|
|
5891
5969
|
heartbeatHealthy = false;
|
|
5892
|
-
if (error instanceof ConnectorDeletedError) {
|
|
5893
|
-
void writeLog(
|
|
5970
|
+
if (error instanceof ConnectorDeletedError || error instanceof ConnectorUnauthorizedError) {
|
|
5971
|
+
void writeLog(
|
|
5972
|
+
"warn",
|
|
5973
|
+
error instanceof ConnectorDeletedError ? "connector deleted remotely; stopping daemon" : "connector authentication rejected remotely; stopping daemon"
|
|
5974
|
+
);
|
|
5894
5975
|
void shutdown({ unregister: false });
|
|
5895
5976
|
return;
|
|
5896
5977
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buda-ai/connector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"lint:err": "biome format . --write && biome check . --write --diagnostic-level=error"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@bunny-agent/daemon": "0.9.
|
|
30
|
+
"@bunny-agent/daemon": "0.9.57",
|
|
31
31
|
"@clack/prompts": "^1.0.0",
|
|
32
32
|
"playwright-core": "^1.52.0",
|
|
33
33
|
"ws": "^8.21.0"
|