@lesscap/baton-cli 0.2.6 → 0.2.8

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/baton.mjs +73 -19
  2. package/package.json +1 -1
package/dist/baton.mjs CHANGED
@@ -700,6 +700,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
700
700
  import { join } from "node:path";
701
701
  var PROJECT_CONFIG_NAME = ".baton.json";
702
702
  var projectConfigPath = (cwd = process.cwd()) => join(cwd, PROJECT_CONFIG_NAME);
703
+ var configPathFromArgs = (args, cwd) => args.config ?? projectConfigPath(cwd);
703
704
  var loadProjectConfig = (path) => {
704
705
  try {
705
706
  return JSON.parse(readFileSync(path, "utf8"));
@@ -768,10 +769,17 @@ var common = {
768
769
  },
769
770
  json: { type: "boolean", description: "output JSON" }
770
771
  };
772
+ var resolveAuth = (env, fileToken) => {
773
+ const envBearer = env.BATON_TOKEN ?? env.BATON_WORKER_TOKEN;
774
+ if (envBearer) return { bearer: envBearer };
775
+ if (env.BATON_USER && env.BATON_PASS) return "cookie";
776
+ return fileToken ? { bearer: fileToken } : void 0;
777
+ };
771
778
  var clientFor = (args) => {
772
- const envAuth = process.env.BATON_TOKEN ?? (process.env.BATON_USER && process.env.BATON_PASS);
773
- const cfgToken = envAuth ? void 0 : loadProjectConfigOrNull(projectConfigPath())?.worker?.apiToken;
774
- return createClient(resolveBaseUrl(args.url), cfgToken ? { bearer: cfgToken } : void 0);
779
+ const baseUrl = resolveBaseUrl(args.url);
780
+ const fileToken = loadProjectConfigOrNull(projectConfigPath())?.worker?.apiToken;
781
+ const auth = resolveAuth(process.env, fileToken);
782
+ return createClient(baseUrl, auth && auth !== "cookie" ? auth : void 0);
775
783
  };
776
784
  var resolveProjectId = (args) => {
777
785
  if (args.project !== void 0 && args.project !== "") return Number(args.project);
@@ -2645,11 +2653,35 @@ var runWorkerDaemon = async (cfg, client, signal) => {
2645
2653
  const worktreeDir = defaultWorktreeDir();
2646
2654
  const children = /* @__PURE__ */ new Map();
2647
2655
  const log = (m) => console.log(`[worker #${cfg.workerId} ${cfg.name}] ${m}`);
2648
- const ping = () => {
2649
- void client.workers.heartbeat(cfg.machineId).catch((e) => log(`heartbeat failed: ${String(e)}`));
2656
+ const HEARTBEAT_MS = 3e4;
2657
+ const HEARTBEAT_TIMEOUT_MS = 15e3;
2658
+ const MAX_HEARTBEAT_FAILURES = 5;
2659
+ let heartbeatFailures = 0;
2660
+ let fatal = false;
2661
+ let stop = () => {
2662
+ };
2663
+ const ping = async () => {
2664
+ try {
2665
+ await Promise.race([
2666
+ client.workers.heartbeat(cfg.machineId),
2667
+ new Promise(
2668
+ (_, reject) => setTimeout(() => reject(new Error("heartbeat timeout")), HEARTBEAT_TIMEOUT_MS)
2669
+ )
2670
+ ]);
2671
+ heartbeatFailures = 0;
2672
+ } catch (e) {
2673
+ heartbeatFailures += 1;
2674
+ log(`heartbeat failed (${heartbeatFailures}/${MAX_HEARTBEAT_FAILURES}): ${String(e)}`);
2675
+ if (heartbeatFailures >= MAX_HEARTBEAT_FAILURES) {
2676
+ const downSec = MAX_HEARTBEAT_FAILURES * HEARTBEAT_MS / 1e3;
2677
+ log(`heartbeat down ~${downSec}s \u2014 exiting so the supervisor restarts a clean worker`);
2678
+ fatal = true;
2679
+ stop();
2680
+ }
2681
+ }
2650
2682
  };
2651
- ping();
2652
- const hb = setInterval(ping, 3e4);
2683
+ void ping();
2684
+ const hb = setInterval(() => void ping(), HEARTBEAT_MS);
2653
2685
  const killChild = (child) => {
2654
2686
  if (child.pid === void 0) return;
2655
2687
  try {
@@ -2769,12 +2801,14 @@ var runWorkerDaemon = async (cfg, client, signal) => {
2769
2801
  es.onerror = () => log("command stream error (eventsource will retry)");
2770
2802
  log(`listening for commands (server ${cfg.server}, repo ${repo})`);
2771
2803
  await new Promise((resolve2) => {
2804
+ stop = resolve2;
2772
2805
  if (signal.aborted) return resolve2();
2773
2806
  signal.addEventListener("abort", () => resolve2(), { once: true });
2774
2807
  });
2775
2808
  es.close();
2776
2809
  clearInterval(hb);
2777
2810
  for (const { child } of children.values()) killChild(child);
2811
+ if (fatal) process.exit(1);
2778
2812
  };
2779
2813
 
2780
2814
  // src/worker/machine-id.ts
@@ -2837,6 +2871,10 @@ var worker = defineCommand({
2837
2871
  args: {
2838
2872
  project: { type: "string", description: "project id (overrides .baton.json)" },
2839
2873
  name: { type: "string", description: "worker display name (default: hostname)" },
2874
+ config: {
2875
+ type: "string",
2876
+ description: "path to the worker config file (default: ./.baton.json)"
2877
+ },
2840
2878
  ...common
2841
2879
  },
2842
2880
  run: async ({ args }) => {
@@ -2850,13 +2888,17 @@ var worker = defineCommand({
2850
2888
  const hostname = osHostname();
2851
2889
  const machineId = readOrCreateMachineId();
2852
2890
  const name = args.name ?? hostname;
2853
- const { out, configPath } = await registerWorker(c, {
2854
- projectId: resolveProjectId(args),
2855
- name,
2856
- server,
2857
- hostname,
2858
- machineId
2859
- });
2891
+ const { out, configPath } = await registerWorker(
2892
+ c,
2893
+ {
2894
+ projectId: resolveProjectId(args),
2895
+ name,
2896
+ server,
2897
+ hostname,
2898
+ machineId
2899
+ },
2900
+ configPathFromArgs(args)
2901
+ );
2860
2902
  if (args.json) {
2861
2903
  console.log(toJson({ ...out, configPath, machineIdPath: machineIdPath() }));
2862
2904
  return;
@@ -2873,9 +2915,15 @@ var worker = defineCommand({
2873
2915
  name: "run",
2874
2916
  description: "run the persistent worker daemon (listens for session create/delete commands)"
2875
2917
  },
2876
- args: { ...common },
2877
- run: async () => {
2878
- const cfg = viewWorker(loadProjectConfig(projectConfigPath()));
2918
+ args: {
2919
+ config: {
2920
+ type: "string",
2921
+ description: "path to the worker config file (default: ./.baton.json)"
2922
+ },
2923
+ ...common
2924
+ },
2925
+ run: async ({ args }) => {
2926
+ const cfg = viewWorker(loadProjectConfig(configPathFromArgs(args)));
2879
2927
  const client = createClient(cfg.server, { bearer: cfg.apiToken });
2880
2928
  const ac = new AbortController();
2881
2929
  const stop = () => ac.abort();
@@ -2944,11 +2992,17 @@ var worker = defineCommand({
2944
2992
  }),
2945
2993
  whoami: defineCommand({
2946
2994
  meta: { name: "whoami", description: "show local worker config from .baton.json" },
2947
- args: { ...common },
2995
+ args: {
2996
+ config: {
2997
+ type: "string",
2998
+ description: "path to the worker config file (default: ./.baton.json)"
2999
+ },
3000
+ ...common
3001
+ },
2948
3002
  run: ({ args }) => {
2949
3003
  const cfg = (() => {
2950
3004
  try {
2951
- return loadProjectConfig(projectConfigPath());
3005
+ return loadProjectConfig(configPathFromArgs(args));
2952
3006
  } catch {
2953
3007
  return null;
2954
3008
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lesscap/baton-cli",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "baton CLI — register/run a worker, manage projects/requirements/tasks/sessions",