usebeeline 0.1.31 → 0.1.34

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/usebeeline.mjs +93 -48
  2. package/package.json +1 -1
@@ -5804,14 +5804,13 @@ var init_self_update = __esm({
5804
5804
  fetchImpl;
5805
5805
  log;
5806
5806
  stageOnly;
5807
- nextCheckAllowedAt = 0;
5808
5807
  disposed = false;
5809
5808
  applying = false;
5810
5809
  deferredBusyNotice = false;
5811
5810
  lastVerdictLog = "";
5812
5811
  constructor(options) {
5813
5812
  const env = options.env ?? process.env;
5814
- const numberEnv2 = (name, fallback) => {
5813
+ const numberEnv = (name, fallback) => {
5815
5814
  const raw = env[name];
5816
5815
  if (!raw)
5817
5816
  return fallback;
@@ -5820,8 +5819,7 @@ var init_self_update = __esm({
5820
5819
  };
5821
5820
  this.options = {
5822
5821
  layout: options.layout,
5823
- checkIntervalMs: options.checkIntervalMs ?? numberEnv2("BEELINE_UPDATE_INTERVAL_MS", 6 * 60 * 6e4),
5824
- idleTimeoutMs: options.idleTimeoutMs ?? numberEnv2("BEELINE_UPDATE_IDLE_TIMEOUT_MS", 30 * 6e4),
5822
+ idleTimeoutMs: options.idleTimeoutMs ?? numberEnv("BEELINE_UPDATE_IDLE_TIMEOUT_MS", 30 * 6e4),
5825
5823
  idlePollMs: options.idlePollMs ?? 5e3,
5826
5824
  env,
5827
5825
  now: options.now ?? Date.now
@@ -5846,7 +5844,7 @@ var init_self_update = __esm({
5846
5844
  return;
5847
5845
  const now2 = this.options.now();
5848
5846
  const disabled = this.options.env.BEELINE_UPDATE_DISABLE === "1";
5849
- if (disabled || now2 < this.nextCheckAllowedAt)
5847
+ if (disabled)
5850
5848
  return;
5851
5849
  this.applying = true;
5852
5850
  try {
@@ -18819,6 +18817,7 @@ var AgentChannel = class {
18819
18817
  this.#registeredAt = Date.now();
18820
18818
  this.#registrations += 1;
18821
18819
  this.handlers.onOpen();
18820
+ this.machine.registered();
18822
18821
  for (const listener of [...this.#openListeners]) {
18823
18822
  try {
18824
18823
  listener();
@@ -18890,6 +18889,8 @@ var MachineLink = class {
18890
18889
  link;
18891
18890
  #channels = /* @__PURE__ */ new Map();
18892
18891
  #identity = { releaseVersion: "v0.0.0" };
18892
+ #reconciledOnSocket = false;
18893
+ #pendingRelease;
18893
18894
  random;
18894
18895
  constructor(options) {
18895
18896
  this.options = options;
@@ -18899,11 +18900,15 @@ var MachineLink = class {
18899
18900
  protocols: () => [MACHINE_SOCKET_PROTOCOL],
18900
18901
  factory: options.factory ?? ((url, protocols, socketOptions) => new wrapper_default(url, protocols, socketOptions)),
18901
18902
  onOpen: () => {
18903
+ this.#reconciledOnSocket = false;
18904
+ this.#pendingRelease = void 0;
18902
18905
  for (const channel of this.#channels.values())
18903
18906
  channel.register();
18904
18907
  },
18905
18908
  onMessage: (data) => this.#message(data),
18906
18909
  onClose: () => {
18910
+ this.#reconciledOnSocket = false;
18911
+ this.#pendingRelease = void 0;
18907
18912
  for (const channel of this.#channels.values())
18908
18913
  channel.socketClosed();
18909
18914
  },
@@ -18921,6 +18926,18 @@ var MachineLink = class {
18921
18926
  ...identity.sourceSha ? { sourceSha: identity.sourceSha } : {}
18922
18927
  };
18923
18928
  }
18929
+ /** The first accepted registration follows the server's cached release frame. */
18930
+ registered() {
18931
+ if (this.#reconciledOnSocket)
18932
+ return;
18933
+ this.#reconciledOnSocket = true;
18934
+ this.options.onRegistered?.();
18935
+ if (this.#pendingRelease) {
18936
+ const release = this.#pendingRelease;
18937
+ this.#pendingRelease = void 0;
18938
+ this.#broadcast(release);
18939
+ }
18940
+ }
18924
18941
  /** A later channel for the same agent (an in-process restart) replaces the earlier one. */
18925
18942
  attach(agentId, handlers) {
18926
18943
  return new AgentChannel(this, agentId, handlers);
@@ -18974,13 +18991,19 @@ var MachineLink = class {
18974
18991
  return;
18975
18992
  }
18976
18993
  if (event.type === "helper-release") {
18977
- for (const channel of this.#channels.values())
18978
- channel.broadcast(event);
18994
+ if (!this.#reconciledOnSocket)
18995
+ this.#pendingRelease = event;
18996
+ else
18997
+ this.#broadcast(event);
18979
18998
  return;
18980
18999
  }
18981
19000
  if (event.type !== "hello" && event.type !== "force-update")
18982
19001
  this.#logUnknown(event.type);
18983
19002
  }
19003
+ #broadcast(event) {
19004
+ for (const channel of this.#channels.values())
19005
+ channel.broadcast(event);
19006
+ }
18984
19007
  };
18985
19008
 
18986
19009
  // apps/body/dist/agent-sign-in.js
@@ -30549,7 +30572,6 @@ import { createHash as createHash8 } from "node:crypto";
30549
30572
  import { mkdir as mkdir13, rm as rm10, stat as stat8, writeFile as writeFile10 } from "node:fs/promises";
30550
30573
  import { dirname as dirname13, resolve as resolve16 } from "node:path";
30551
30574
  var UPDATE_CONVERGENCE_SLO_MS = 10 * 6e4;
30552
- var DEFAULT_UPDATE_INTERVAL_MS = 3e4;
30553
30575
  var UPDATE_DRAIN_DEADLINE_MS = 5 * 6e4;
30554
30576
  var MAX_HOST_RESTART_STAGGER_MS = 15e3;
30555
30577
  function managedRestartStaggerMs(agentId, desiredRelease, drainDeadlineAt, now2 = Date.now()) {
@@ -30559,7 +30581,6 @@ function managedRestartStaggerMs(agentId, desiredRelease, drainDeadlineAt, now2
30559
30581
  }
30560
30582
  var UPDATE_WORKER_DEADLINE_MS = UPDATE_CONVERGENCE_SLO_MS - 6e4;
30561
30583
  var LOCK_STALE_MS = UPDATE_WORKER_DEADLINE_MS + 5 * 6e4;
30562
- var DEFAULT_UPDATE_INITIAL_DELAY_MS = 0;
30563
30584
  async function withInstallLock(layout, work, options = {}) {
30564
30585
  const now2 = options.now ?? Date.now;
30565
30586
  const lock = resolve16(layout.releasesRoot, ".state", "install.lock");
@@ -30645,8 +30666,9 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
30645
30666
  #now;
30646
30667
  #env;
30647
30668
  #runUpdateWorker;
30669
+ #onWorkerSettled;
30648
30670
  #drainDeadlineMs;
30649
- #nextUpdateCheckAt;
30671
+ #checkRequested = false;
30650
30672
  #updateCycleDeadlineAt;
30651
30673
  #worker;
30652
30674
  #requested = false;
@@ -30659,8 +30681,8 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
30659
30681
  this.#now = options.now ?? Date.now;
30660
30682
  this.#env = options.env ?? process.env;
30661
30683
  this.#runUpdateWorker = options.runUpdateWorker ?? runManagedUpdateWorkerProcess;
30684
+ this.#onWorkerSettled = options.onWorkerSettled;
30662
30685
  this.#drainDeadlineMs = options.drainDeadlineMs ?? UPDATE_DRAIN_DEADLINE_MS;
30663
- this.#nextUpdateCheckAt = this.#now() + numberEnv(this.#env, "BEELINE_UPDATE_INITIAL_DELAY_MS", DEFAULT_UPDATE_INITIAL_DELAY_MS);
30664
30686
  }
30665
30687
  static async create(layout, now2 = Date.now, options = {}) {
30666
30688
  const env = options.env ?? process.env;
@@ -30676,7 +30698,11 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
30676
30698
  if (!releaseKey || this.#notifiedReleaseKey === releaseKey)
30677
30699
  return;
30678
30700
  this.#notifiedReleaseKey = releaseKey;
30679
- this.#nextUpdateCheckAt = 0;
30701
+ this.#checkRequested = true;
30702
+ }
30703
+ /** A fresh socket covers notices missed while disconnected with one manifest read. */
30704
+ reconcileOnConnect() {
30705
+ this.#checkRequested = true;
30680
30706
  }
30681
30707
  /**
30682
30708
  * Return true once the active release differs from the serving release.
@@ -30691,50 +30717,49 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
30691
30717
  async check() {
30692
30718
  if (this.#requested || !this.#loadedRelease)
30693
30719
  return this.#requested;
30694
- if (this.#stagedReleaseId)
30695
- return false;
30696
30720
  const desiredRelease = await activeReleaseId(this.#layout).catch(() => void 0);
30697
30721
  if (desiredRelease && desiredRelease !== this.#loadedRelease) {
30698
30722
  return this.#armRestart(desiredRelease);
30699
30723
  }
30700
- const state = await readUpdateState(this.#layout);
30701
- if (state.stagedReleaseId && state.stagedReleaseId !== this.#loadedRelease) {
30702
- const requestedAt = this.#now();
30703
- this.#stagedReleaseId = state.stagedReleaseId;
30704
- this.#restartRequest = {
30705
- loadedRelease: this.#loadedRelease,
30706
- desiredRelease: state.stagedReleaseId,
30707
- drainDeadlineAt: requestedAt + this.#drainDeadlineMs
30708
- };
30709
- console.log(`[thin-core] staged update ${state.stagedReleaseId} is waiting for active turns to drain`);
30710
- return false;
30711
- }
30712
- const now2 = this.#now();
30713
- if (this.#env.BEELINE_UPDATE_DISABLE === "1" || now2 < this.#nextUpdateCheckAt)
30724
+ if (this.#worker)
30714
30725
  return false;
30715
- this.#nextUpdateCheckAt = now2 + numberEnv(this.#env, "BEELINE_UPDATE_INTERVAL_MS", DEFAULT_UPDATE_INTERVAL_MS);
30716
- if (!this.#worker) {
30726
+ if (this.#env.BEELINE_UPDATE_DISABLE !== "1" && this.#checkRequested) {
30727
+ this.#checkRequested = false;
30728
+ const now2 = this.#now();
30729
+ const notifiedReleaseKey = this.#notifiedReleaseKey;
30717
30730
  this.#updateCycleDeadlineAt = now2 + this.#drainDeadlineMs;
30718
30731
  this.#worker = withInstallLock(this.#layout, async () => {
30719
30732
  const current = await activeReleaseId(this.#layout).catch(() => void 0);
30720
30733
  if (current && current !== this.#loadedRelease)
30721
30734
  return;
30722
- const state2 = await readUpdateState(this.#layout);
30723
- const interval = numberEnv(this.#env, "BEELINE_UPDATE_INTERVAL_MS", DEFAULT_UPDATE_INTERVAL_MS);
30724
- if (state2.notifiedReleaseKey === this.#notifiedReleaseKey && state2.lastCheckAt !== void 0 && now2 - state2.lastCheckAt < interval)
30725
- return;
30726
30735
  await this.#runUpdateWorker();
30727
- if (this.#notifiedReleaseKey) {
30736
+ if (notifiedReleaseKey) {
30728
30737
  await writeUpdateState(this.#layout, {
30729
30738
  ...await readUpdateState(this.#layout),
30730
- notifiedReleaseKey: this.#notifiedReleaseKey
30739
+ notifiedReleaseKey
30731
30740
  });
30732
30741
  }
30733
30742
  }).catch((error) => {
30734
30743
  console.error(`[thin-core] update worker failed: ${error instanceof Error ? error.message : String(error)}`);
30735
30744
  }).finally(() => {
30736
30745
  this.#worker = void 0;
30746
+ this.#onWorkerSettled?.();
30737
30747
  });
30748
+ return false;
30749
+ }
30750
+ if (this.#stagedReleaseId)
30751
+ return false;
30752
+ const state = await readUpdateState(this.#layout);
30753
+ if (state.stagedReleaseId && state.stagedReleaseId !== this.#loadedRelease) {
30754
+ const requestedAt = this.#now();
30755
+ this.#stagedReleaseId = state.stagedReleaseId;
30756
+ this.#restartRequest = {
30757
+ loadedRelease: this.#loadedRelease,
30758
+ desiredRelease: state.stagedReleaseId,
30759
+ drainDeadlineAt: requestedAt + this.#drainDeadlineMs
30760
+ };
30761
+ console.log(`[thin-core] staged update ${state.stagedReleaseId} is waiting for active turns to drain`);
30762
+ return false;
30738
30763
  }
30739
30764
  return false;
30740
30765
  }
@@ -30823,6 +30848,7 @@ var ManagedUpdateDrain = class {
30823
30848
  #clearTimer;
30824
30849
  #waitLogTimer;
30825
30850
  #resolving;
30851
+ #rerunRequested = false;
30826
30852
  #restarting = false;
30827
30853
  constructor(options) {
30828
30854
  this.#options = options;
@@ -30837,10 +30863,21 @@ var ManagedUpdateDrain = class {
30837
30863
  #resolve() {
30838
30864
  if (this.#restarting)
30839
30865
  return Promise.resolve("restarting");
30840
- if (this.#resolving)
30866
+ if (this.#resolving) {
30867
+ this.#rerunRequested = true;
30841
30868
  return this.#resolving;
30869
+ }
30842
30870
  this.#resolving = this.#step().finally(() => {
30843
30871
  this.#resolving = void 0;
30872
+ if (this.#rerunRequested) {
30873
+ this.#rerunRequested = false;
30874
+ void this.#resolve().catch((error) => {
30875
+ if (this.#options.failed)
30876
+ this.#options.failed(error);
30877
+ else
30878
+ this.#log(`[thin-core] update drain failed: ${String(error)}`);
30879
+ });
30880
+ }
30844
30881
  });
30845
30882
  return this.#resolving;
30846
30883
  }
@@ -30891,10 +30928,6 @@ function updateWaitMs(options, request) {
30891
30928
  const remaining = request.drainDeadlineAt - (options.now ?? Date.now)();
30892
30929
  return remaining > 0 ? remaining : UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS;
30893
30930
  }
30894
- function numberEnv(env, name, fallback) {
30895
- const value = Number(env[name]);
30896
- return Number.isFinite(value) && value >= 0 ? value : fallback;
30897
- }
30898
30931
  async function runManagedUpdateWorker() {
30899
30932
  const { beelineInstallLayout: beelineInstallLayout2 } = await Promise.resolve().then(() => (init_self_update(), self_update_exports));
30900
30933
  const layout = beelineInstallLayout2(process.env);
@@ -31014,8 +31047,8 @@ ${import_picocolors.default.dim("Usage:")}
31014
31047
  beeline update --force Apply even when the comparison is indeterminate
31015
31048
  beeline update --manifest-url <u> Override the published manifest URL
31016
31049
 
31017
- The supervised daemon also checks automatically (every BEELINE_UPDATE_INTERVAL_MS,
31018
- default 30s) with the same busy gate; BEELINE_UPDATE_DISABLE=1 turns the
31050
+ The supervised helper checks the manifest on connection and on release notices,
31051
+ then waits for a quiet gap to restart. BEELINE_UPDATE_DISABLE=1 turns the
31019
31052
  automatic path off. \`beeline update\` always works.
31020
31053
  `);
31021
31054
  }
@@ -36061,8 +36094,11 @@ async function shareChromiumPage(input) {
36061
36094
  if (message.method === "Page.frameNavigated" && message.params?.frame?.url) {
36062
36095
  try {
36063
36096
  const url = new URL(message.params.frame.url);
36064
- if (url.origin !== "null" && relay.readyState === import_websocket.default.OPEN)
36065
- relay.send(JSON.stringify({ type: "share-event", name: "navigation", detail: url.origin }));
36097
+ if (["http:", "https:"].includes(url.protocol) && relay.readyState === import_websocket.default.OPEN) {
36098
+ url.username = "";
36099
+ url.password = "";
36100
+ relay.send(JSON.stringify({ type: "share-event", name: "navigation", detail: url.href }));
36101
+ }
36066
36102
  } catch {
36067
36103
  }
36068
36104
  return;
@@ -49756,7 +49792,6 @@ async function runAgentRuntime(configPath, host, signal) {
49756
49792
  host.notifyReleaseAvailable(`${version}:${sha}`);
49757
49793
  void host.tickUpdate();
49758
49794
  });
49759
- daemonApi.onLiveOpen(() => void host.tickUpdate());
49760
49795
  core.setInteractiveIdleListener(() => {
49761
49796
  void host.tickUpdate();
49762
49797
  });
@@ -50142,7 +50177,11 @@ async function runMachineHelper(options = {}) {
50142
50177
  const machineKey = () => [...slots.values()].map((slot) => slot.agentId).sort()[0] ?? "helper";
50143
50178
  if (layout) {
50144
50179
  update = await ManagedUpdateHandoff.create(layout, Date.now, {
50145
- env
50180
+ env,
50181
+ ...options.runUpdateWorker ? { runUpdateWorker: options.runUpdateWorker } : {},
50182
+ onWorkerSettled: () => {
50183
+ void tickUpdate();
50184
+ }
50146
50185
  });
50147
50186
  }
50148
50187
  const recoverManagedRestart = (error) => {
@@ -50188,7 +50227,13 @@ async function runMachineHelper(options = {}) {
50188
50227
  machineLink: (baseUrl) => {
50189
50228
  let link2 = links.get(baseUrl);
50190
50229
  if (!link2) {
50191
- link2 = new MachineLink({ baseUrl });
50230
+ link2 = new MachineLink({
50231
+ baseUrl,
50232
+ onRegistered: () => {
50233
+ update?.reconcileOnConnect();
50234
+ void tickUpdate();
50235
+ }
50236
+ });
50192
50237
  link2.setHelperIdentity({
50193
50238
  releaseVersion: loadedReleaseIdentity?.version,
50194
50239
  sourceSha: loadedReleaseIdentity?.commit
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usebeeline",
3
- "version": "0.1.31",
3
+ "version": "0.1.34",
4
4
  "description": "Connect an AI coding agent to Beeline with one command.",
5
5
  "homepage": "https://usebeeline.app",
6
6
  "bugs": {