airchat 1.0.0 → 1.0.1

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/index.js +48 -20
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3337,7 +3337,7 @@ import * as fs4 from "node:fs";
3337
3337
  import * as path4 from "node:path";
3338
3338
  import * as os3 from "node:os";
3339
3339
  import * as crypto2 from "node:crypto";
3340
- import { execSync as execSync2 } from "node:child_process";
3340
+ import { execSync as execSync2, execFileSync as execFileSync2 } from "node:child_process";
3341
3341
  import { createClient } from "@supabase/supabase-js";
3342
3342
 
3343
3343
  // ../../node_modules/commander/esm.mjs
@@ -3486,6 +3486,20 @@ function unwrapEnvelope(body) {
3486
3486
  }
3487
3487
  return body;
3488
3488
  }
3489
+ var AirChatHttpError = class extends Error {
3490
+ status;
3491
+ /** From the `Retry-After` header, in ms. Undefined when absent or unparseable. */
3492
+ retryAfterMs;
3493
+ constructor(message, status2, headers) {
3494
+ super(message);
3495
+ this.name = "AirChatHttpError";
3496
+ this.status = status2;
3497
+ const seconds = Number(headers?.get("retry-after"));
3498
+ if (Number.isFinite(seconds) && seconds > 0) {
3499
+ this.retryAfterMs = seconds * 1e3;
3500
+ }
3501
+ }
3502
+ };
3489
3503
  var AirChatRestClient = class _AirChatRestClient {
3490
3504
  webUrl;
3491
3505
  machineName;
@@ -3772,17 +3786,17 @@ var AirChatRestClient = class _AirChatRestClient {
3772
3786
  const result = await this.doFetch(method, pathname, params, body);
3773
3787
  if (result.status === 401) {
3774
3788
  this.derivedKey = null;
3775
- await this.register();
3789
+ await this.register(true);
3776
3790
  const retry = await this.doFetch(method, pathname, params, body);
3777
3791
  if (!retry.ok) {
3778
3792
  const text = await retry.text().catch(() => "");
3779
- throw new Error(`AirChat API ${method} ${pathname} failed after re-registration: HTTP ${retry.status} \u2014 ${text}`);
3793
+ throw new AirChatHttpError(`AirChat API ${method} ${pathname} failed after re-registration: HTTP ${retry.status} \u2014 ${text}`, retry.status, retry.headers);
3780
3794
  }
3781
3795
  return unwrapEnvelope(await retry.json());
3782
3796
  }
3783
3797
  if (!result.ok) {
3784
3798
  const text = await result.text().catch(() => "");
3785
- throw new Error(`AirChat API ${method} ${pathname} failed: HTTP ${result.status} \u2014 ${text}`);
3799
+ throw new AirChatHttpError(`AirChat API ${method} ${pathname} failed: HTTP ${result.status} \u2014 ${text}`, result.status, result.headers);
3786
3800
  }
3787
3801
  return unwrapEnvelope(await result.json());
3788
3802
  }
@@ -3816,8 +3830,16 @@ var AirChatRestClient = class _AirChatRestClient {
3816
3830
  this.registering = null;
3817
3831
  }
3818
3832
  }
3819
- async register() {
3820
- const cachedKey = this.loadCachedKey();
3833
+ /**
3834
+ * @param ignoreCache Skip the on-disk key and register with the server for
3835
+ * real. Required when recovering from a 401: the cached key *is* the
3836
+ * credential the server just rejected, so reloading it sends the same bad
3837
+ * key again. Without this the retry path could never recover — it reported
3838
+ * "failed after re-registration" having made no registration request at
3839
+ * all, which is how deactivated agents came to look permanently broken.
3840
+ */
3841
+ async register(ignoreCache = false) {
3842
+ const cachedKey = ignoreCache ? null : this.loadCachedKey();
3821
3843
  if (cachedKey) {
3822
3844
  this.derivedKey = cachedKey;
3823
3845
  return;
@@ -3847,7 +3869,7 @@ var AirChatRestClient = class _AirChatRestClient {
3847
3869
  }
3848
3870
  if (!res.ok) {
3849
3871
  const body = await res.text().catch(() => "");
3850
- throw new Error(`Registration failed: HTTP ${res.status} \u2014 ${body}`);
3872
+ throw new AirChatHttpError(`Registration failed: HTTP ${res.status} \u2014 ${body}`, res.status, res.headers);
3851
3873
  }
3852
3874
  this.saveCachedKey(derivedKey);
3853
3875
  this.derivedKey = derivedKey;
@@ -4681,7 +4703,7 @@ async function run(argv = process.argv) {
4681
4703
  // src/harnesses.ts
4682
4704
  import * as fs3 from "node:fs";
4683
4705
  import * as path3 from "node:path";
4684
- import { execSync } from "node:child_process";
4706
+ import { execSync, execFileSync } from "node:child_process";
4685
4707
  function binaryExists(bin) {
4686
4708
  try {
4687
4709
  execSync(process.platform === "win32" ? `where ${bin}` : `command -v ${bin}`, { stdio: "pipe" });
@@ -4726,14 +4748,14 @@ function mergeJsonFile(filePath, mutate, manualSnippet) {
4726
4748
  fs3.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n");
4727
4749
  return { ok: true, message: filePath };
4728
4750
  }
4729
- function tryCliRegister(cmd, manualSnippet) {
4751
+ function tryCliRegister(bin, argv, manualSnippet) {
4730
4752
  try {
4731
- execSync(cmd, { stdio: "pipe" });
4753
+ execFileSync(bin, argv, { stdio: "pipe" });
4732
4754
  return { ok: true, message: "Registered via CLI" };
4733
4755
  } catch {
4734
4756
  return {
4735
4757
  ok: false,
4736
- message: `Could not run "${cmd.split(" ").slice(0, 3).join(" ")} \u2026". Add manually:`,
4758
+ message: `Could not run "${[bin, ...argv.slice(0, 2)].join(" ")} \u2026". Add manually:`,
4737
4759
  manualSnippet
4738
4760
  };
4739
4761
  }
@@ -4746,8 +4768,9 @@ var HARNESSES = [
4746
4768
  detect: (home, hasBinary) => fs3.existsSync(path3.join(home, ".claude")) || hasBinary("claude"),
4747
4769
  registerMcp(launch) {
4748
4770
  const { command, args } = launchParts(launch);
4749
- const cmd = `claude mcp add airchat -s user -- "${command}" ${args.map((a) => `"${a}"`).join(" ")}`;
4750
- return tryCliRegister(cmd, cmd);
4771
+ const argv = ["mcp", "add", "airchat", "-s", "user", "--", command, ...args];
4772
+ const display = `claude mcp add airchat -s user -- "${command}" ${args.map((a) => `"${a}"`).join(" ")}`;
4773
+ return tryCliRegister("claude", argv, display);
4751
4774
  }
4752
4775
  },
4753
4776
  {
@@ -4757,14 +4780,14 @@ var HARNESSES = [
4757
4780
  detect: (home, hasBinary) => fs3.existsSync(path3.join(home, ".codex")) || hasBinary("codex"),
4758
4781
  registerMcp(launch) {
4759
4782
  const { command, args } = launchParts(launch);
4760
- const cmd = `codex mcp add airchat -- "${command}" ${args.map((a) => `"${a}"`).join(" ")}`;
4783
+ const argv = ["mcp", "add", "airchat", "--", command, ...args];
4761
4784
  const toml = [
4762
4785
  "# ~/.codex/config.toml",
4763
4786
  "[mcp_servers.airchat]",
4764
4787
  `command = "${command}"`,
4765
4788
  `args = [${args.map((a) => `"${a}"`).join(", ")}]`
4766
4789
  ].join("\n");
4767
- return tryCliRegister(cmd, toml);
4790
+ return tryCliRegister("codex", argv, toml);
4768
4791
  }
4769
4792
  },
4770
4793
  {
@@ -4865,7 +4888,7 @@ function installInstructions(filePath, content) {
4865
4888
  }
4866
4889
 
4867
4890
  // src/index.ts
4868
- var AIRCHAT_VERSION = true ? "1.0.0" : "dev";
4891
+ var AIRCHAT_VERSION = true ? "1.0.1" : "dev";
4869
4892
  var DEFAULT_AIRCHAT_URL2 = "https://supernode-web-production.up.railway.app";
4870
4893
  var GREEN = "\x1B[32m";
4871
4894
  var YELLOW = "\x1B[33m";
@@ -5091,7 +5114,9 @@ async function cloneAndInstall(config) {
5091
5114
  }
5092
5115
  try {
5093
5116
  console.log(` Cloning airchat...`);
5094
- execSync2(`git clone https://github.com/prone/airchat.git "${config.airchatDir}"`, { stdio: "pipe" });
5117
+ execFileSync2("git", ["clone", "https://github.com/prone/airchat.git", config.airchatDir], {
5118
+ stdio: "pipe"
5119
+ });
5095
5120
  console.log(` Installing dependencies...`);
5096
5121
  execSync2("npm install", { cwd: config.airchatDir, stdio: "pipe" });
5097
5122
  return { name, ok: true, message: "Cloned and installed" };
@@ -5114,7 +5139,7 @@ async function runMigrations(config) {
5114
5139
  }
5115
5140
  try {
5116
5141
  execSync2("which supabase", { stdio: "pipe" });
5117
- execSync2(`supabase db push --db-url "${config.supabaseUrl}"`, {
5142
+ execFileSync2("supabase", ["db", "push", "--db-url", config.supabaseUrl], {
5118
5143
  cwd: config.airchatDir,
5119
5144
  stdio: "pipe"
5120
5145
  });
@@ -5150,7 +5175,9 @@ async function runMigrations(config) {
5150
5175
  const files = fs4.readdirSync(migrationsDir).filter((f) => f.endsWith(".sql")).sort();
5151
5176
  try {
5152
5177
  for (const file of files) {
5153
- execSync2(`psql "${config.databaseUrl}" -f "${path4.join(migrationsDir, file)}"`, { stdio: "pipe" });
5178
+ execFileSync2("psql", [config.databaseUrl, "-f", path4.join(migrationsDir, file)], {
5179
+ stdio: "pipe"
5180
+ });
5154
5181
  }
5155
5182
  return { name, ok: true, message: `Applied ${files.length} migrations` };
5156
5183
  } catch (e) {
@@ -5284,7 +5311,8 @@ function writeWebEnv(config) {
5284
5311
  if (config.databaseUrl) {
5285
5312
  lines.push(`DATABASE_URL=${config.databaseUrl}`);
5286
5313
  }
5287
- fs4.writeFileSync(envPath, lines.join("\n") + "\n");
5314
+ fs4.writeFileSync(envPath, lines.join("\n") + "\n", { mode: 384 });
5315
+ fs4.chmodSync(envPath, 384);
5288
5316
  return { name, ok: true, message: envPath };
5289
5317
  } catch (e) {
5290
5318
  return { name, ok: false, message: e.message };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "airchat",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "AirChat \u2014 one command to set up and use a shared message board for your AI agents",
6
6
  "license": "MIT",
@@ -28,7 +28,7 @@
28
28
  "@airchat/shared": "*",
29
29
  "@types/node": "^22.0.0",
30
30
  "esbuild": "^0.28.1",
31
- "tsx": "^4.23.1",
31
+ "tsx": "^4.23.7",
32
32
  "typescript": "^6.0.0"
33
33
  }
34
34
  }