@buda-ai/connector 0.1.24 → 0.1.25
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 +87 -11
- package/package.json +1 -1
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.25",
|
|
527
527
|
private: false,
|
|
528
528
|
type: "module",
|
|
529
529
|
publishConfig: {
|
|
@@ -2601,6 +2601,7 @@ async function ensureStableConnectorRuntime() {
|
|
|
2601
2601
|
// src/daemon-client.ts
|
|
2602
2602
|
var START_TIMEOUT_MS = 45e3;
|
|
2603
2603
|
var BOOTSTRAP_TIMEOUT_MS = 5 * 6e4;
|
|
2604
|
+
var LOCK_ONLY_RECOVERY_MAX_AGE_MS = 5 * 6e4;
|
|
2604
2605
|
var POLL_INTERVAL_MS = 250;
|
|
2605
2606
|
var wait2 = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
2606
2607
|
var getConfigRoot = () => process.env.BUDA_CONNECTOR_CONFIG_ROOT;
|
|
@@ -2668,8 +2669,13 @@ async function spawnDetachedDaemonProcess(command, args, options, dependencies =
|
|
|
2668
2669
|
});
|
|
2669
2670
|
try {
|
|
2670
2671
|
await waitForChildSpawn(child);
|
|
2672
|
+
if (!child.pid) {
|
|
2673
|
+
throw Object.assign(new Error("spawned Connector daemon did not expose a PID"), {
|
|
2674
|
+
code: "NO_PID"
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2671
2677
|
child.unref();
|
|
2672
|
-
return strategy.name;
|
|
2678
|
+
return { pid: child.pid, strategy: strategy.name };
|
|
2673
2679
|
} catch (error) {
|
|
2674
2680
|
const code = getErrorCode(error);
|
|
2675
2681
|
const shouldRetry = platform4 === "win32" && index === 0 && code === "EINVAL";
|
|
@@ -2688,14 +2694,18 @@ async function spawnDetachedDaemonProcess(command, args, options, dependencies =
|
|
|
2688
2694
|
}
|
|
2689
2695
|
throw new Error("Connector daemon process creation failed without a spawn attempt");
|
|
2690
2696
|
}
|
|
2697
|
+
var readReadyDaemon = async (launchConfig) => {
|
|
2698
|
+
const config = await readDeviceConfig(launchConfig, getConfigRoot());
|
|
2699
|
+
if (!config) return null;
|
|
2700
|
+
const status = await readLiveStatus(config);
|
|
2701
|
+
return status ? { config, status } : null;
|
|
2702
|
+
};
|
|
2691
2703
|
var waitForDaemon = async (launchConfig, deadline) => {
|
|
2692
2704
|
let lastConfig = null;
|
|
2693
2705
|
while (Date.now() < deadline) {
|
|
2706
|
+
const ready = await readReadyDaemon(launchConfig);
|
|
2707
|
+
if (ready) return ready;
|
|
2694
2708
|
lastConfig = await readDeviceConfig(launchConfig, getConfigRoot());
|
|
2695
|
-
if (lastConfig) {
|
|
2696
|
-
const status = await readLiveStatus(lastConfig);
|
|
2697
|
-
if (status) return { config: lastConfig, status };
|
|
2698
|
-
}
|
|
2699
2709
|
await wait2(POLL_INTERVAL_MS);
|
|
2700
2710
|
}
|
|
2701
2711
|
throw new Error(
|
|
@@ -2705,7 +2715,7 @@ var waitForDaemon = async (launchConfig, deadline) => {
|
|
|
2705
2715
|
var spawnInternalDaemon = async (launchConfig, mode, preparedRuntime, options = {}) => {
|
|
2706
2716
|
const runtime = preparedRuntime ?? await ensureStableConnectorRuntime();
|
|
2707
2717
|
const debugDaemonStdio = process.env.BUDA_CONNECTOR_DEBUG_DAEMON_STDIO === "1";
|
|
2708
|
-
|
|
2718
|
+
return spawnDetachedDaemonProcess(
|
|
2709
2719
|
runtime.nodeExecutable,
|
|
2710
2720
|
getRuntimeCommandArgs(runtime, mode, options),
|
|
2711
2721
|
{
|
|
@@ -2717,6 +2727,70 @@ var spawnInternalDaemon = async (launchConfig, mode, preparedRuntime, options =
|
|
|
2717
2727
|
}
|
|
2718
2728
|
);
|
|
2719
2729
|
};
|
|
2730
|
+
var waitForProcessExit = async (pid, timeoutMs) => {
|
|
2731
|
+
const deadline = Date.now() + timeoutMs;
|
|
2732
|
+
while (Date.now() < deadline) {
|
|
2733
|
+
if (!isProcessAlive2(pid)) return true;
|
|
2734
|
+
await wait2(50);
|
|
2735
|
+
}
|
|
2736
|
+
return !isProcessAlive2(pid);
|
|
2737
|
+
};
|
|
2738
|
+
var signalProcess = (pid, signal) => {
|
|
2739
|
+
try {
|
|
2740
|
+
process.kill(pid, signal);
|
|
2741
|
+
return true;
|
|
2742
|
+
} catch (error) {
|
|
2743
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH") {
|
|
2744
|
+
return false;
|
|
2745
|
+
}
|
|
2746
|
+
throw error;
|
|
2747
|
+
}
|
|
2748
|
+
};
|
|
2749
|
+
var stopUnresponsiveDaemonProcess = async (pid) => {
|
|
2750
|
+
if (!isProcessAlive2(pid)) return;
|
|
2751
|
+
if (!signalProcess(pid, "SIGTERM")) return;
|
|
2752
|
+
if (await waitForProcessExit(pid, 2e3)) return;
|
|
2753
|
+
if (!signalProcess(pid, "SIGKILL")) return;
|
|
2754
|
+
if (await waitForProcessExit(pid, 3e3)) return;
|
|
2755
|
+
throw new Error(`Timed-out Connector daemon PID ${pid} could not be stopped safely`);
|
|
2756
|
+
};
|
|
2757
|
+
var readDaemonLockOwnerPid = async (config) => {
|
|
2758
|
+
const lockPath = `${getConnectorDaemonLockTarget(
|
|
2759
|
+
config.serverUrl,
|
|
2760
|
+
config.connectorId,
|
|
2761
|
+
getConfigRoot()
|
|
2762
|
+
)}.lock`;
|
|
2763
|
+
const raw = await readFile6(lockPath, "utf8").catch(() => "");
|
|
2764
|
+
try {
|
|
2765
|
+
const metadata = JSON.parse(raw);
|
|
2766
|
+
const age = typeof metadata.createdAt === "number" && Number.isFinite(metadata.createdAt) ? Date.now() - metadata.createdAt : Number.POSITIVE_INFINITY;
|
|
2767
|
+
return typeof metadata.pid === "number" && Number.isSafeInteger(metadata.pid) && metadata.pid > 0 && age >= 0 && age <= LOCK_ONLY_RECOVERY_MAX_AGE_MS ? metadata.pid : null;
|
|
2768
|
+
} catch {
|
|
2769
|
+
return null;
|
|
2770
|
+
}
|
|
2771
|
+
};
|
|
2772
|
+
async function startDaemonWithRecovery(dependencies, maxAttempts = 2) {
|
|
2773
|
+
let lastError;
|
|
2774
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
2775
|
+
const spawned = await dependencies.spawnDaemon();
|
|
2776
|
+
try {
|
|
2777
|
+
return await dependencies.waitForReady();
|
|
2778
|
+
} catch (error) {
|
|
2779
|
+
lastError = error;
|
|
2780
|
+
const lateReady = await dependencies.readReady().catch(() => null);
|
|
2781
|
+
if (lateReady) return lateReady;
|
|
2782
|
+
await dependencies.stopDaemon(spawned.pid);
|
|
2783
|
+
if (attempt === maxAttempts) throw error;
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
throw lastError;
|
|
2787
|
+
}
|
|
2788
|
+
var startInternalDaemonWithRecovery = (launchConfig, mode, preparedRuntime, options = {}) => startDaemonWithRecovery({
|
|
2789
|
+
readReady: () => readReadyDaemon(launchConfig),
|
|
2790
|
+
spawnDaemon: () => spawnInternalDaemon(launchConfig, mode, preparedRuntime, options),
|
|
2791
|
+
stopDaemon: stopUnresponsiveDaemonProcess,
|
|
2792
|
+
waitForReady: () => waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS)
|
|
2793
|
+
});
|
|
2720
2794
|
var stopOlderIdleDaemon = async (config, status) => {
|
|
2721
2795
|
if (!shouldReplaceDaemonVersion(status.version, CONNECTOR_RUNTIME_VERSION)) return false;
|
|
2722
2796
|
if (!status.idle) return false;
|
|
@@ -2749,8 +2823,7 @@ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
|
|
|
2749
2823
|
const status = mode === "persistent" ? await requestDaemonControl(existingConfig, "persist", getConfigRoot()) : existingStatus;
|
|
2750
2824
|
return { config: existingConfig, status };
|
|
2751
2825
|
}
|
|
2752
|
-
await
|
|
2753
|
-
return await waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS);
|
|
2826
|
+
return await startInternalDaemonWithRecovery(launchConfig, mode, preparedRuntime, options);
|
|
2754
2827
|
}
|
|
2755
2828
|
const persistedStatus = await readPersistedDaemonStatus(
|
|
2756
2829
|
existingConfig,
|
|
@@ -2761,9 +2834,12 @@ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
|
|
|
2761
2834
|
`Connector daemon PID ${persistedStatus.pid} is running but its local control channel is not responding. Stop that process and retry.`
|
|
2762
2835
|
);
|
|
2763
2836
|
}
|
|
2837
|
+
const lockOwnerPid = await readDaemonLockOwnerPid(existingConfig);
|
|
2838
|
+
if (lockOwnerPid && isProcessAlive2(lockOwnerPid)) {
|
|
2839
|
+
await stopUnresponsiveDaemonProcess(lockOwnerPid);
|
|
2840
|
+
}
|
|
2764
2841
|
}
|
|
2765
|
-
await
|
|
2766
|
-
return await waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS);
|
|
2842
|
+
return await startInternalDaemonWithRecovery(launchConfig, mode, void 0, options);
|
|
2767
2843
|
} finally {
|
|
2768
2844
|
await bootstrapLock.release();
|
|
2769
2845
|
}
|