@buda-ai/connector 0.1.11 → 0.1.14

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.
Files changed (2) hide show
  1. package/dist/cli.js +83 -38
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -517,7 +517,7 @@ import path2 from "path";
517
517
  // package.json
518
518
  var package_default = {
519
519
  name: "@buda-ai/connector",
520
- version: "0.1.11",
520
+ version: "0.1.14",
521
521
  private: false,
522
522
  type: "module",
523
523
  publishConfig: {
@@ -2357,6 +2357,20 @@ async function registerConnector(config, launchConfig, bunnyDaemon, options) {
2357
2357
  relayProtocol: result.relayProtocol
2358
2358
  };
2359
2359
  }
2360
+ async function registerConnectorForExplicitEnrollment(config, launchConfig, bunnyDaemon) {
2361
+ try {
2362
+ return await registerConnector(config, launchConfig, bunnyDaemon);
2363
+ } catch (error) {
2364
+ const hasSpaceToken = Boolean(config.token?.trim() || getConnectorToken(launchConfig));
2365
+ const hasOauthToken = Boolean(launchConfig?.oauthToken?.trim());
2366
+ if (!(error instanceof ConnectorDeletedError) || !hasSpaceToken || hasOauthToken) {
2367
+ throw error;
2368
+ }
2369
+ return registerConnector(config, launchConfig, bunnyDaemon, {
2370
+ recoverMissingProfile: true
2371
+ });
2372
+ }
2373
+ }
2360
2374
  async function heartbeatConnector(config, launchConfig, bunnyDaemon) {
2361
2375
  const logs = takePendingLogs();
2362
2376
  const result = await postJson(
@@ -2545,13 +2559,14 @@ var isProcessAlive2 = (pid) => {
2545
2559
  return !(typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH");
2546
2560
  }
2547
2561
  };
2548
- var getRuntimeCommandArgs = (runtime, mode) => [
2562
+ var getRuntimeCommandArgs = (runtime, mode, options = {}) => [
2549
2563
  ...runtime.nodeArgs ?? [],
2550
2564
  runtime.entrypoint,
2551
2565
  "daemon",
2552
2566
  "--internal-daemon",
2553
2567
  "--daemon-mode",
2554
2568
  mode,
2569
+ ...options.explicitEnrollment ? ["--explicit-enrollment"] : [],
2555
2570
  "--plain"
2556
2571
  ];
2557
2572
  var compareVersions = (left, right) => {
@@ -2578,9 +2593,9 @@ var waitForDaemon = async (launchConfig, deadline) => {
2578
2593
  lastConfig ? "Connector daemon did not expose its control channel in time" : "Connector registration did not create a canonical local identity in time"
2579
2594
  );
2580
2595
  };
2581
- var spawnInternalDaemon = async (launchConfig, mode, preparedRuntime) => {
2596
+ var spawnInternalDaemon = async (launchConfig, mode, preparedRuntime, options = {}) => {
2582
2597
  const runtime = preparedRuntime ?? await ensureStableConnectorRuntime();
2583
- const child = spawn(runtime.nodeExecutable, getRuntimeCommandArgs(runtime, mode), {
2598
+ const child = spawn(runtime.nodeExecutable, getRuntimeCommandArgs(runtime, mode, options), {
2584
2599
  detached: true,
2585
2600
  env: {
2586
2601
  ...process.env,
@@ -2603,7 +2618,7 @@ var stopOlderIdleDaemon = async (config, status) => {
2603
2618
  }
2604
2619
  throw new Error("Older Connector daemon did not stop cleanly; existing identity was preserved");
2605
2620
  };
2606
- async function ensureConnectorDaemon(launchConfig, mode) {
2621
+ async function ensureConnectorDaemon(launchConfig, mode, options = {}) {
2607
2622
  const deviceId = await getOrCreateSystemDeviceId(getConfigRoot());
2608
2623
  const scope = await resolveConnectorScope(deviceId, launchConfig);
2609
2624
  launchConfig = { ...launchConfig, spaceId: scope.spaceId };
@@ -2623,7 +2638,7 @@ async function ensureConnectorDaemon(launchConfig, mode) {
2623
2638
  const status = mode === "persistent" ? await requestDaemonControl(existingConfig, "persist", getConfigRoot()) : existingStatus;
2624
2639
  return { config: existingConfig, status };
2625
2640
  }
2626
- await spawnInternalDaemon(launchConfig, mode, preparedRuntime);
2641
+ await spawnInternalDaemon(launchConfig, mode, preparedRuntime, options);
2627
2642
  return await waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS);
2628
2643
  }
2629
2644
  const persistedStatus = await readPersistedDaemonStatus(
@@ -2636,7 +2651,7 @@ async function ensureConnectorDaemon(launchConfig, mode) {
2636
2651
  );
2637
2652
  }
2638
2653
  }
2639
- await spawnInternalDaemon(launchConfig, mode);
2654
+ await spawnInternalDaemon(launchConfig, mode, void 0, options);
2640
2655
  return await waitForDaemon(launchConfig, Date.now() + START_TIMEOUT_MS);
2641
2656
  } finally {
2642
2657
  await bootstrapLock.release();
@@ -2780,7 +2795,9 @@ async function runConnectorDashboard(launchConfig, options = {}) {
2780
2795
  plain: options.plain
2781
2796
  });
2782
2797
  if (!dashboard) {
2783
- const { config } = await ensureConnectorDaemon(launchConfig, "persistent");
2798
+ const { config } = await ensureConnectorDaemon(launchConfig, "persistent", {
2799
+ explicitEnrollment: true
2800
+ });
2784
2801
  const stopLogs2 = streamDaemonLogs({
2785
2802
  connectorId: config.connectorId,
2786
2803
  dashboardActive: false,
@@ -2835,7 +2852,7 @@ async function runConnectorDashboard(launchConfig, options = {}) {
2835
2852
  preferredConnectorId = config.connectorId;
2836
2853
  }).catch(() => void 0).finally(() => refresh());
2837
2854
  const timer = setInterval(() => void refresh(), 1e3);
2838
- void ensureConnectorDaemon(launchConfig, "persistent").then(async ({ config }) => {
2855
+ void ensureConnectorDaemon(launchConfig, "persistent", { explicitEnrollment: true }).then(async ({ config }) => {
2839
2856
  pendingConnector = void 0;
2840
2857
  preferredConnectorId = config.connectorId;
2841
2858
  await refresh();
@@ -3003,13 +3020,12 @@ var createTerminalSession = async (grant, nodePtyLoader) => {
3003
3020
  try {
3004
3021
  nodePty = await nodePtyLoader();
3005
3022
  } catch (error) {
3023
+ const message = serializeErrorMessage(error);
3006
3024
  void writeLog(
3007
3025
  "error",
3008
3026
  `local terminal could not load node-pty: ${serializeErrorMessage(error)}`
3009
3027
  );
3010
- throw new LocalTerminalUnavailableError(
3011
- "Local terminal is unavailable because node-pty could not be loaded. The connector remains available for other capabilities."
3012
- );
3028
+ throw new LocalTerminalUnavailableError(message);
3013
3029
  }
3014
3030
  const shell = process.platform === "win32" ? "powershell.exe" : process.env.SHELL || "/bin/bash";
3015
3031
  const shellArgs = process.platform === "win32" ? [] : ["-l"];
@@ -3196,17 +3212,13 @@ var proxyConnectorTerminalTunnel = async (input) => {
3196
3212
  headers: { "X-Buda-Local-Terminal-Token": input.localToken },
3197
3213
  handshakeTimeout: TERMINAL_HANDSHAKE_TIMEOUT_MS
3198
3214
  });
3199
- try {
3200
- await Promise.all([connectSocket(cloud), connectSocket(local)]);
3201
- } catch (error) {
3202
- cloud.terminate();
3203
- local.terminate();
3204
- throw error;
3205
- }
3206
3215
  let bridgeAccepted = false;
3216
+ let activityStarted = false;
3207
3217
  let activityEnded = false;
3218
+ let localCloseAfterBridge = null;
3219
+ let localCloseAfterOpen = null;
3208
3220
  const endActivity = () => {
3209
- if (activityEnded) return;
3221
+ if (!activityStarted || activityEnded) return;
3210
3222
  activityEnded = true;
3211
3223
  input.onActivityEnd?.();
3212
3224
  };
@@ -3228,7 +3240,9 @@ var proxyConnectorTerminalTunnel = async (input) => {
3228
3240
  if (!bridgeAccepted) {
3229
3241
  if (!isBinary && data.toString() === '{"type":"bridge-accepted"}') {
3230
3242
  bridgeAccepted = true;
3231
- local.send(JSON.stringify({ type: "bridge-start" }));
3243
+ if (local.readyState === WebSocket.OPEN) {
3244
+ local.send(JSON.stringify({ type: "bridge-start" }));
3245
+ }
3232
3246
  for (const [frame, binary] of pendingCloudFrames) {
3233
3247
  if (local.readyState === WebSocket.OPEN) local.send(frame, { binary });
3234
3248
  }
@@ -3238,6 +3252,9 @@ var proxyConnectorTerminalTunnel = async (input) => {
3238
3252
  pendingCloudFrames.length = 0;
3239
3253
  pendingLocalFrames.length = 0;
3240
3254
  pendingBytes = 0;
3255
+ if (localCloseAfterBridge && cloud.readyState === WebSocket.OPEN) {
3256
+ cloud.close(localCloseAfterBridge.code, localCloseAfterBridge.reason);
3257
+ }
3241
3258
  return;
3242
3259
  }
3243
3260
  bufferFrame(pendingCloudFrames, data, isBinary);
@@ -3254,25 +3271,51 @@ var proxyConnectorTerminalTunnel = async (input) => {
3254
3271
  });
3255
3272
  cloud.once("close", (code, reason) => {
3256
3273
  endActivity();
3274
+ const normalizedCode = normalizeTerminalCloseCode(code);
3275
+ const normalizedReason = truncateTerminalCloseReason(reason.toString());
3257
3276
  if (!bridgeAccepted) {
3258
3277
  void writeLog(
3259
3278
  "warn",
3260
- `terminal cloud tunnel closed before readiness session=${input.sessionId} code=${code} reason=${truncateTerminalCloseReason(reason.toString())}`
3279
+ `terminal cloud tunnel closed before readiness session=${input.sessionId} code=${code} reason=${normalizedReason}`
3261
3280
  );
3262
3281
  }
3263
3282
  if (local.readyState === WebSocket.OPEN) {
3264
- local.close(normalizeTerminalCloseCode(code), truncateTerminalCloseReason(reason.toString()));
3283
+ local.close(normalizedCode, normalizedReason);
3284
+ } else if (local.readyState === WebSocket.CONNECTING) {
3285
+ localCloseAfterOpen = { code: normalizedCode, reason: normalizedReason };
3265
3286
  }
3266
3287
  });
3267
3288
  local.once("close", (code, reason) => {
3268
3289
  endActivity();
3269
- if (cloud.readyState === WebSocket.OPEN) {
3270
- cloud.close(normalizeTerminalCloseCode(code), truncateTerminalCloseReason(reason.toString()));
3290
+ const normalizedClose = {
3291
+ code: normalizeTerminalCloseCode(code),
3292
+ reason: truncateTerminalCloseReason(reason.toString())
3293
+ };
3294
+ if (!bridgeAccepted) {
3295
+ localCloseAfterBridge = normalizedClose;
3296
+ } else if (cloud.readyState === WebSocket.OPEN) {
3297
+ cloud.close(normalizedClose.code, normalizedClose.reason);
3298
+ }
3299
+ });
3300
+ local.once("open", () => {
3301
+ if (localCloseAfterOpen && local.readyState === WebSocket.OPEN) {
3302
+ local.close(localCloseAfterOpen.code, localCloseAfterOpen.reason);
3271
3303
  }
3272
3304
  });
3273
3305
  cloud.once("error", () => local.terminate());
3274
3306
  local.once("error", () => cloud.terminate());
3275
- input.onActivityStart?.();
3307
+ try {
3308
+ await Promise.all([connectSocket(cloud), connectSocket(local)]);
3309
+ } catch (error) {
3310
+ cloud.terminate();
3311
+ local.terminate();
3312
+ throw error;
3313
+ }
3314
+ if (cloud.readyState !== WebSocket.OPEN) return;
3315
+ if (local.readyState === WebSocket.OPEN) {
3316
+ activityStarted = true;
3317
+ input.onActivityStart?.();
3318
+ }
3276
3319
  cloud.send(JSON.stringify({ type: "bridge-ready" }));
3277
3320
  await writeLog(
3278
3321
  "info",
@@ -5396,9 +5439,7 @@ async function runOnboard(launchConfig, options = {}) {
5396
5439
  await migrateLegacyWorkspaceRoot(config2.workdirRoot);
5397
5440
  await provisionWorkdirLayout(config2.workdirRoot);
5398
5441
  try {
5399
- const registration = await registerConnector(config2, launchConfig, void 0, {
5400
- recoverMissingProfile: false
5401
- });
5442
+ const registration = await registerConnectorForExplicitEnrollment(config2, launchConfig);
5402
5443
  config2.connectorId = registration.connectorId;
5403
5444
  config2.spaceId = registration.spaceId ?? config2.spaceId;
5404
5445
  relayProtocol = registration.relayProtocol;
@@ -5430,7 +5471,16 @@ async function runOnboard(launchConfig, options = {}) {
5430
5471
  if (!existing) {
5431
5472
  } else {
5432
5473
  try {
5433
- relayProtocol = (await registerConnector(config, launchConfig, bunnyDaemon)).relayProtocol;
5474
+ const registration = await registerConnectorForExplicitEnrollment(
5475
+ config,
5476
+ launchConfig,
5477
+ bunnyDaemon
5478
+ );
5479
+ config.connectorId = registration.connectorId;
5480
+ config.spaceId = registration.spaceId ?? config.spaceId;
5481
+ if (!config.spaceId) throw new Error("Registration did not resolve a Space");
5482
+ relayProtocol = registration.relayProtocol;
5483
+ await writeDeviceConfig(config);
5434
5484
  } catch (error) {
5435
5485
  await stopEmbeddedBunnyDaemon(bunnyDaemon);
5436
5486
  s.stop("Failed to register");
@@ -5518,15 +5568,12 @@ async function stopRecoveredIdleDaemon(config, configRoot) {
5518
5568
  throw new Error(`Recovered Connector ${config.connectorId} did not stop cleanly.`);
5519
5569
  }
5520
5570
  async function runNewConnector(launchConfig) {
5521
- const existing = await readDeviceConfig(launchConfig);
5522
5571
  const config = await ensureDeviceConfig(launchConfig);
5523
5572
  const paths = getConnectorPaths(config.serverUrl);
5524
5573
  await migrateLegacyWorkspaceRoot(config.workdirRoot);
5525
5574
  await provisionWorkdirLayout(config.workdirRoot);
5526
5575
  await LocalWorkspaceRegistry.open(config.workdirRoot);
5527
- const registration = await registerConnector(config, launchConfig, void 0, {
5528
- recoverMissingProfile: shouldRecoverMissingProfile(Boolean(existing), launchConfig)
5529
- });
5576
+ const registration = await registerConnectorForExplicitEnrollment(config, launchConfig);
5530
5577
  config.connectorId = registration.connectorId;
5531
5578
  config.spaceId = registration.spaceId ?? config.spaceId;
5532
5579
  await writeDeviceConfig(config);
@@ -5567,9 +5614,7 @@ async function runConnectorDaemon(launchConfig, options = {}) {
5567
5614
  const workspaceRegistry = await LocalWorkspaceRegistry.open(config.workdirRoot);
5568
5615
  bunnyDaemon = await startEmbeddedBunnyDaemon(config, launchConfig);
5569
5616
  const activeBunnyDaemon = bunnyDaemon;
5570
- const registration = await registerConnector(config, launchConfig, activeBunnyDaemon, {
5571
- recoverMissingProfile: shouldRecoverMissingProfile(Boolean(existing), launchConfig)
5572
- });
5617
+ const registration = options.explicitEnrollment ? await registerConnectorForExplicitEnrollment(config, launchConfig, activeBunnyDaemon) : await registerConnector(config, launchConfig, activeBunnyDaemon);
5573
5618
  const connectorIdChanged = config.connectorId !== registration.connectorId;
5574
5619
  if (connectorIdChanged) {
5575
5620
  const registeredConfig = {
@@ -5678,7 +5723,6 @@ async function runConnectorDaemon(launchConfig, options = {}) {
5678
5723
  throw error;
5679
5724
  }
5680
5725
  }
5681
- var shouldRecoverMissingProfile = (hasExistingProfile, launchConfig) => !hasExistingProfile && Boolean(getConnectorToken(launchConfig)) && !launchConfig?.oauthToken?.trim();
5682
5726
 
5683
5727
  // src/cli.ts
5684
5728
  function printHelp() {
@@ -5770,6 +5814,7 @@ async function main() {
5770
5814
  if (command === "daemon" && process.argv.includes("--internal-daemon")) {
5771
5815
  await runConnectorDaemon(launchConfig, {
5772
5816
  captureConsole: true,
5817
+ explicitEnrollment: process.argv.includes("--explicit-enrollment"),
5773
5818
  mode: getDaemonModeFlag(),
5774
5819
  version: CONNECTOR_RUNTIME_VERSION
5775
5820
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buda-ai/connector",
3
- "version": "0.1.11",
3
+ "version": "0.1.14",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "publishConfig": {