@nocoo/bat 3.3.0 → 3.4.0

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/bat.js +153 -6
  2. package/package.json +1 -1
package/dist/bat.js CHANGED
@@ -3093,7 +3093,7 @@ var init_connect = () => {};
3093
3093
  var init_settings = () => {};
3094
3094
 
3095
3095
  // ../shared/dist/version.js
3096
- var BAT_VERSION = "3.3.0";
3096
+ var BAT_VERSION = "3.4.0";
3097
3097
 
3098
3098
  // ../shared/dist/workstations.js
3099
3099
  function fail(error) {
@@ -3726,7 +3726,7 @@ function validateWorkstationPairStart(input) {
3726
3726
  }
3727
3727
  };
3728
3728
  }
3729
- var WORKSTATION_INTERVAL_SECONDS = 600, WORKSTATION_PING_COUNT = 3, WORKSTATION_MAX_TARGETS = 100, WORKSTATION_BODY_MAX_BYTES = 65536, WORKSTATION_REPORT_SKEW_SECONDS = 300, WORKSTATION_MAX_RUN_MS = 120000, WORKSTATION_STALE_AFTER_SECONDS, WORKSTATION_NAME_MAX_LENGTH = 64, WORKSTATION_PAIRING_TTL_SECONDS = 300, WORKSTATION_PAIRING_ALPHABET = "ABCDEFGHJKMNPQRSTVWXYZ23456789", WORKSTATION_PAIRING_LENGTH = 8, WORKSTATION_LIMITS, WORKSTATION_CONNECTIVITY_STATUSES, UUID_PATTERN, IPV4_PATTERN, PKCE_CHALLENGE_PATTERN, PKCE_VERIFIER_PATTERN, WORKSTATION_TOKEN_PATTERN, IPV6_CHARS, REPORT_KEYS, CPU_KEYS, MEM_KEYS, DISK_KEYS, NETWORK_KEYS, INTERFACE_KEYS, ADDRESS_KEYS, CONNECTIVITY_KEYS, TARGET_KEYS, TARGETS_RESPONSE_KEYS, LOGIN_EXCHANGE_KEYS, PAIR_START_KEYS;
3729
+ var WORKSTATION_INTERVAL_SECONDS = 600, WORKSTATION_PING_COUNT = 3, WORKSTATION_MAX_TARGETS = 100, WORKSTATION_BODY_MAX_BYTES = 65536, WORKSTATION_REPORT_SKEW_SECONDS = 300, WORKSTATION_MAX_RUN_MS = 120000, WORKSTATION_STALE_AFTER_SECONDS, WORKSTATION_NAME_MAX_LENGTH = 64, WORKSTATION_PAIRING_TTL_SECONDS = 300, WORKSTATION_PAIRING_ALPHABET = "ABCDEFGHJKMNPQRSTVWXYZ23456789", WORKSTATION_PAIRING_LENGTH = 8, WORKSTATION_LIMITS, WORKSTATION_CONNECTIVITY_STATUSES, WORKSTATION_REPORTS_MAX_LIMIT = 20, WORKSTATION_REPORTS_DEFAULT_LIMIT = 3, UUID_PATTERN, IPV4_PATTERN, PKCE_CHALLENGE_PATTERN, PKCE_VERIFIER_PATTERN, WORKSTATION_TOKEN_PATTERN, IPV6_CHARS, REPORT_KEYS, CPU_KEYS, MEM_KEYS, DISK_KEYS, NETWORK_KEYS, INTERFACE_KEYS, ADDRESS_KEYS, CONNECTIVITY_KEYS, TARGET_KEYS, TARGETS_RESPONSE_KEYS, LOGIN_EXCHANGE_KEYS, PAIR_START_KEYS;
3730
3730
  var init_workstations = __esm(() => {
3731
3731
  WORKSTATION_STALE_AFTER_SECONDS = WORKSTATION_INTERVAL_SECONDS * 3;
3732
3732
  WORKSTATION_LIMITS = {
@@ -4544,7 +4544,7 @@ async function exchangeLogin(origins, input, options = {}) {
4544
4544
  if (!validated.ok) {
4545
4545
  throw new Error(`invalid exchange request: ${validated.error}`);
4546
4546
  }
4547
- const client = new HttpClient(origins.machine, undefined, options.timeoutMs ?? REQUEST_TIMEOUT_MS, options.signal);
4547
+ const client = new HttpClient(origins.machine, undefined, options.timeoutMs ?? REQUEST_TIMEOUT_MS, options.signal, options.fetchImpl);
4548
4548
  const raw = await client.post("/api/workstation-auth/exchange", validated.value);
4549
4549
  return parseExchangeOutput(raw, origins.machine);
4550
4550
  }
@@ -4627,7 +4627,7 @@ async function startPairing(origins, input, options = {}) {
4627
4627
  if (!validated.ok) {
4628
4628
  throw new Error(`invalid pairing request: ${validated.error}`);
4629
4629
  }
4630
- const client = new HttpClient(origins.machine, undefined, options.timeoutMs ?? REQUEST_TIMEOUT_MS, options.signal);
4630
+ const client = new HttpClient(origins.machine, undefined, options.timeoutMs ?? REQUEST_TIMEOUT_MS, options.signal, options.fetchImpl);
4631
4631
  const raw = await client.post("/api/workstation-auth/pair", validated.value);
4632
4632
  if (typeof raw !== "object" || raw === null) {
4633
4633
  throw new Error("pairing start returned no object");
@@ -4654,7 +4654,7 @@ async function pairingStatus(origins, pairingCode, options = {}) {
4654
4654
  if (normalized === null) {
4655
4655
  throw new Error("invalid pairing code");
4656
4656
  }
4657
- const client = new HttpClient(origins.machine, undefined, options.timeoutMs ?? REQUEST_TIMEOUT_MS, options.signal);
4657
+ const client = new HttpClient(origins.machine, undefined, options.timeoutMs ?? REQUEST_TIMEOUT_MS, options.signal, options.fetchImpl);
4658
4658
  const raw = await client.get(`/api/workstation-auth/pair/${normalized}`);
4659
4659
  if (typeof raw !== "object" || raw === null) {
4660
4660
  throw new Error("pairing status returned no object");
@@ -4670,6 +4670,14 @@ async function pairingStatus(origins, pairingCode, options = {}) {
4670
4670
  expires_at: typeof record.expires_at === "number" ? record.expires_at : 0
4671
4671
  };
4672
4672
  }
4673
+ async function fetchReports(workerUrl, token, limit, options = {}) {
4674
+ const client = new HttpClient(workerUrl, token, options.timeoutMs ?? REQUEST_TIMEOUT_MS, options.signal, options.fetchImpl);
4675
+ const raw = await client.get(`/api/workstation-probe/reports?limit=${encodeURIComponent(String(limit))}`);
4676
+ if (typeof raw !== "object" || raw === null || !Array.isArray(raw.data)) {
4677
+ throw new Error("report readback returned no data array");
4678
+ }
4679
+ return raw;
4680
+ }
4673
4681
  var REQUEST_TIMEOUT_MS = 30000, UPLOAD_ATTEMPTS = 3, RETRY_BASE_MS = 500;
4674
4682
  var init_api2 = __esm(() => {
4675
4683
  init_dist4();
@@ -5137,6 +5145,143 @@ var init_scheduler = __esm(() => {
5137
5145
  `);
5138
5146
  });
5139
5147
 
5148
+ // src/workstation/detail.ts
5149
+ function describeLimit(limit) {
5150
+ if (limit === undefined) {
5151
+ return WORKSTATION_REPORTS_DEFAULT_LIMIT;
5152
+ }
5153
+ if (!Number.isInteger(limit) || limit < 1 || limit > WORKSTATION_REPORTS_MAX_LIMIT) {
5154
+ throw new Error(`--limit must be an integer between 1 and ${WORKSTATION_REPORTS_MAX_LIMIT}`);
5155
+ }
5156
+ return limit;
5157
+ }
5158
+ function answering(connectivity) {
5159
+ return connectivity.filter((entry) => entry.status === "reachable" || entry.status === "partial").length;
5160
+ }
5161
+ function averageRtt(entry) {
5162
+ const values = entry.rtts_ms.filter((value) => value !== null);
5163
+ if (values.length === 0) {
5164
+ return "—";
5165
+ }
5166
+ const mean = values.reduce((total, value) => total + value, 0) / values.length;
5167
+ return `${Math.round(mean * 10) / 10} ms`;
5168
+ }
5169
+ function loss(entry) {
5170
+ if (entry.status === "error" || entry.status === "unsupported") {
5171
+ return "—";
5172
+ }
5173
+ if (entry.sent <= 0) {
5174
+ return "—";
5175
+ }
5176
+ return `${Math.round((entry.sent - entry.received) / entry.sent * 1000) / 10}%`;
5177
+ }
5178
+ function clock(seconds) {
5179
+ return new Date(seconds * 1000).toLocaleString();
5180
+ }
5181
+ async function runWorkstationDetail(deps) {
5182
+ const credential = deps.credential ?? readCredential(deps.paths);
5183
+ if (!credential) {
5184
+ error("Not logged in — run 'bat workstation login' first.");
5185
+ return 1;
5186
+ }
5187
+ let limit;
5188
+ try {
5189
+ limit = describeLimit(deps.limit);
5190
+ } catch (err) {
5191
+ error(err.message);
5192
+ return 1;
5193
+ }
5194
+ let response;
5195
+ try {
5196
+ response = await (deps.fetch ?? fetchReports)(credential.worker_url, credential.token, limit);
5197
+ } catch (err) {
5198
+ const message = err.message;
5199
+ error(`Could not read reports: ${message}`);
5200
+ if (/Route not allowed|404|not support/i.test(message)) {
5201
+ info("This deployment does not expose report readback yet; upgrade the server first.");
5202
+ }
5203
+ return 1;
5204
+ }
5205
+ if (deps.json === true) {
5206
+ console.log(JSON.stringify({
5207
+ workstation_id: credential.workstation_id,
5208
+ worker_url: credential.worker_url,
5209
+ limit: response.limit,
5210
+ reports: response.data
5211
+ }, null, 2));
5212
+ return 0;
5213
+ }
5214
+ info(`Workstation ${credential.workstation_id} · ${credential.worker_url}`);
5215
+ const latest = response.data[0];
5216
+ if (!latest) {
5217
+ error("No reports stored yet — run 'bat workstation probe' once.");
5218
+ return 1;
5219
+ }
5220
+ success(`Newest report ${clock(latest.finished_at)} · CPU ${latest.cpu_usage_pct ?? "—"}% · memory ${latest.mem_used_pct}% · ${answering(latest.connectivity)}/${latest.connectivity.length} answering`);
5221
+ table(["Target", "Address", "ICMP", "Average", "Loss", "RTTs"], latest.connectivity.map((entry) => [
5222
+ entry.server_id,
5223
+ `${entry.address} v${entry.family}`,
5224
+ entry.status,
5225
+ averageRtt(entry),
5226
+ loss(entry),
5227
+ entry.rtts_ms.map((value) => value === null ? "—" : `${Math.round(value * 10) / 10}`).join(" / ")
5228
+ ]));
5229
+ if (response.data.length > 1) {
5230
+ info("");
5231
+ info(`Earlier reports (${response.data.length - 1})`);
5232
+ table(["Finished", "CPU", "Memory", "Answering"], response.data.slice(1).map((entry) => [
5233
+ clock(entry.finished_at),
5234
+ entry.cpu_usage_pct === null ? "—" : `${entry.cpu_usage_pct}%`,
5235
+ `${entry.mem_used_pct}%`,
5236
+ `${answering(entry.connectivity)}/${entry.connectivity.length}`
5237
+ ]));
5238
+ }
5239
+ return 0;
5240
+ }
5241
+ var init_detail = __esm(() => {
5242
+ init_dist4();
5243
+ init_output();
5244
+ init_api2();
5245
+ init_store();
5246
+ });
5247
+
5248
+ // src/commands/workstation/detail.ts
5249
+ async function runDetailCommand(deps = {}) {
5250
+ return await runWorkstationDetail({
5251
+ paths: deps.paths ?? getWorkstationPaths(),
5252
+ ...deps.limit !== undefined ? { limit: deps.limit } : {},
5253
+ ...deps.json ? { json: true } : {}
5254
+ });
5255
+ }
5256
+ var detail_default;
5257
+ var init_detail2 = __esm(() => {
5258
+ init_dist3();
5259
+ init_detail();
5260
+ init_store();
5261
+ detail_default = defineCommand({
5262
+ meta: {
5263
+ name: "detail",
5264
+ description: "Show the newest reports and per-target probe results"
5265
+ },
5266
+ args: {
5267
+ limit: {
5268
+ type: "string",
5269
+ description: "How many recent reports to read (1-20, default 3)"
5270
+ },
5271
+ json: {
5272
+ type: "boolean",
5273
+ description: "Print the raw report payload"
5274
+ }
5275
+ },
5276
+ async run({ args }) {
5277
+ process.exitCode = await runDetailCommand({
5278
+ ...typeof args.limit === "string" && args.limit !== "" ? { limit: Number(args.limit) } : {},
5279
+ ...args.json ? { json: true } : {}
5280
+ });
5281
+ }
5282
+ });
5283
+ });
5284
+
5140
5285
  // src/workstation/headless.ts
5141
5286
  async function performHeadlessLogin(deps) {
5142
5287
  const now = deps.now ?? (() => Math.floor(Date.now() / 1000));
@@ -7957,6 +8102,7 @@ var init_status = __esm(() => {
7957
8102
  var workstation_default;
7958
8103
  var init_workstation = __esm(() => {
7959
8104
  init_dist3();
8105
+ init_detail2();
7960
8106
  init_login3();
7961
8107
  init_logout();
7962
8108
  init_probe();
@@ -7969,6 +8115,7 @@ var init_workstation = __esm(() => {
7969
8115
  },
7970
8116
  subCommands: {
7971
8117
  login: login_default2,
8118
+ detail: detail_default,
7972
8119
  setup: setup_default,
7973
8120
  probe: probe_default,
7974
8121
  status: status_default3,
@@ -9496,7 +9643,7 @@ function runChecks(facts) {
9496
9643
  id: "assets-profile",
9497
9644
  label: "Assets profile",
9498
9645
  status: "warn",
9499
- detail: `not configured at ${facts.assetsConfigPath} (optional; run 'bat login')`
9646
+ detail: `not configured at ${facts.assetsConfigPath} — only agent, asset, binding and heartbeat commands need it (run 'bat login')`
9500
9647
  });
9501
9648
  } else {
9502
9649
  const invalid = validateConfig(facts.assetsConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocoo/bat",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "bat CLI — manage agents, assets and bindings for bat.hexly.ai",
5
5
  "type": "module",
6
6
  "bin": {