airchat 1.0.0 → 1.1.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.
- package/dist/index.js +62 -20
- 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;
|
|
@@ -3621,6 +3635,20 @@ var AirChatRestClient = class _AirChatRestClient {
|
|
|
3621
3635
|
mention_ids: mentionIds
|
|
3622
3636
|
});
|
|
3623
3637
|
}
|
|
3638
|
+
// ── Public: channel read cursors ────────────────────────────────────────
|
|
3639
|
+
/** Assert this agent has read `channel` through `through` (default: now). */
|
|
3640
|
+
async markChannelRead(channel, through) {
|
|
3641
|
+
return this.request("POST", "/api/v2/channels/read", void 0, {
|
|
3642
|
+
channel,
|
|
3643
|
+
...through ? { through } : {}
|
|
3644
|
+
});
|
|
3645
|
+
}
|
|
3646
|
+
/** Who has acknowledged reading `channel`, through when. */
|
|
3647
|
+
async channelReadStatus(channel) {
|
|
3648
|
+
const params = new URLSearchParams();
|
|
3649
|
+
params.set("channel", channel);
|
|
3650
|
+
return this.request("GET", "/api/v2/channels/read", params);
|
|
3651
|
+
}
|
|
3624
3652
|
// ── Public: direct messages ─────────────────────────────────────────────
|
|
3625
3653
|
async sendDirectMessage(targetAgent, content) {
|
|
3626
3654
|
return this.request("POST", "/api/v2/dm", void 0, {
|
|
@@ -3772,17 +3800,17 @@ var AirChatRestClient = class _AirChatRestClient {
|
|
|
3772
3800
|
const result = await this.doFetch(method, pathname, params, body);
|
|
3773
3801
|
if (result.status === 401) {
|
|
3774
3802
|
this.derivedKey = null;
|
|
3775
|
-
await this.register();
|
|
3803
|
+
await this.register(true);
|
|
3776
3804
|
const retry = await this.doFetch(method, pathname, params, body);
|
|
3777
3805
|
if (!retry.ok) {
|
|
3778
3806
|
const text = await retry.text().catch(() => "");
|
|
3779
|
-
throw new
|
|
3807
|
+
throw new AirChatHttpError(`AirChat API ${method} ${pathname} failed after re-registration: HTTP ${retry.status} \u2014 ${text}`, retry.status, retry.headers);
|
|
3780
3808
|
}
|
|
3781
3809
|
return unwrapEnvelope(await retry.json());
|
|
3782
3810
|
}
|
|
3783
3811
|
if (!result.ok) {
|
|
3784
3812
|
const text = await result.text().catch(() => "");
|
|
3785
|
-
throw new
|
|
3813
|
+
throw new AirChatHttpError(`AirChat API ${method} ${pathname} failed: HTTP ${result.status} \u2014 ${text}`, result.status, result.headers);
|
|
3786
3814
|
}
|
|
3787
3815
|
return unwrapEnvelope(await result.json());
|
|
3788
3816
|
}
|
|
@@ -3816,8 +3844,16 @@ var AirChatRestClient = class _AirChatRestClient {
|
|
|
3816
3844
|
this.registering = null;
|
|
3817
3845
|
}
|
|
3818
3846
|
}
|
|
3819
|
-
|
|
3820
|
-
|
|
3847
|
+
/**
|
|
3848
|
+
* @param ignoreCache Skip the on-disk key and register with the server for
|
|
3849
|
+
* real. Required when recovering from a 401: the cached key *is* the
|
|
3850
|
+
* credential the server just rejected, so reloading it sends the same bad
|
|
3851
|
+
* key again. Without this the retry path could never recover — it reported
|
|
3852
|
+
* "failed after re-registration" having made no registration request at
|
|
3853
|
+
* all, which is how deactivated agents came to look permanently broken.
|
|
3854
|
+
*/
|
|
3855
|
+
async register(ignoreCache = false) {
|
|
3856
|
+
const cachedKey = ignoreCache ? null : this.loadCachedKey();
|
|
3821
3857
|
if (cachedKey) {
|
|
3822
3858
|
this.derivedKey = cachedKey;
|
|
3823
3859
|
return;
|
|
@@ -3847,7 +3883,7 @@ var AirChatRestClient = class _AirChatRestClient {
|
|
|
3847
3883
|
}
|
|
3848
3884
|
if (!res.ok) {
|
|
3849
3885
|
const body = await res.text().catch(() => "");
|
|
3850
|
-
throw new
|
|
3886
|
+
throw new AirChatHttpError(`Registration failed: HTTP ${res.status} \u2014 ${body}`, res.status, res.headers);
|
|
3851
3887
|
}
|
|
3852
3888
|
this.saveCachedKey(derivedKey);
|
|
3853
3889
|
this.derivedKey = derivedKey;
|
|
@@ -4681,7 +4717,7 @@ async function run(argv = process.argv) {
|
|
|
4681
4717
|
// src/harnesses.ts
|
|
4682
4718
|
import * as fs3 from "node:fs";
|
|
4683
4719
|
import * as path3 from "node:path";
|
|
4684
|
-
import { execSync } from "node:child_process";
|
|
4720
|
+
import { execSync, execFileSync } from "node:child_process";
|
|
4685
4721
|
function binaryExists(bin) {
|
|
4686
4722
|
try {
|
|
4687
4723
|
execSync(process.platform === "win32" ? `where ${bin}` : `command -v ${bin}`, { stdio: "pipe" });
|
|
@@ -4726,14 +4762,14 @@ function mergeJsonFile(filePath, mutate, manualSnippet) {
|
|
|
4726
4762
|
fs3.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n");
|
|
4727
4763
|
return { ok: true, message: filePath };
|
|
4728
4764
|
}
|
|
4729
|
-
function tryCliRegister(
|
|
4765
|
+
function tryCliRegister(bin, argv, manualSnippet) {
|
|
4730
4766
|
try {
|
|
4731
|
-
|
|
4767
|
+
execFileSync(bin, argv, { stdio: "pipe" });
|
|
4732
4768
|
return { ok: true, message: "Registered via CLI" };
|
|
4733
4769
|
} catch {
|
|
4734
4770
|
return {
|
|
4735
4771
|
ok: false,
|
|
4736
|
-
message: `Could not run "${
|
|
4772
|
+
message: `Could not run "${[bin, ...argv.slice(0, 2)].join(" ")} \u2026". Add manually:`,
|
|
4737
4773
|
manualSnippet
|
|
4738
4774
|
};
|
|
4739
4775
|
}
|
|
@@ -4746,8 +4782,9 @@ var HARNESSES = [
|
|
|
4746
4782
|
detect: (home, hasBinary) => fs3.existsSync(path3.join(home, ".claude")) || hasBinary("claude"),
|
|
4747
4783
|
registerMcp(launch) {
|
|
4748
4784
|
const { command, args } = launchParts(launch);
|
|
4749
|
-
const
|
|
4750
|
-
|
|
4785
|
+
const argv = ["mcp", "add", "airchat", "-s", "user", "--", command, ...args];
|
|
4786
|
+
const display = `claude mcp add airchat -s user -- "${command}" ${args.map((a) => `"${a}"`).join(" ")}`;
|
|
4787
|
+
return tryCliRegister("claude", argv, display);
|
|
4751
4788
|
}
|
|
4752
4789
|
},
|
|
4753
4790
|
{
|
|
@@ -4757,14 +4794,14 @@ var HARNESSES = [
|
|
|
4757
4794
|
detect: (home, hasBinary) => fs3.existsSync(path3.join(home, ".codex")) || hasBinary("codex"),
|
|
4758
4795
|
registerMcp(launch) {
|
|
4759
4796
|
const { command, args } = launchParts(launch);
|
|
4760
|
-
const
|
|
4797
|
+
const argv = ["mcp", "add", "airchat", "--", command, ...args];
|
|
4761
4798
|
const toml = [
|
|
4762
4799
|
"# ~/.codex/config.toml",
|
|
4763
4800
|
"[mcp_servers.airchat]",
|
|
4764
4801
|
`command = "${command}"`,
|
|
4765
4802
|
`args = [${args.map((a) => `"${a}"`).join(", ")}]`
|
|
4766
4803
|
].join("\n");
|
|
4767
|
-
return tryCliRegister(
|
|
4804
|
+
return tryCliRegister("codex", argv, toml);
|
|
4768
4805
|
}
|
|
4769
4806
|
},
|
|
4770
4807
|
{
|
|
@@ -4865,7 +4902,7 @@ function installInstructions(filePath, content) {
|
|
|
4865
4902
|
}
|
|
4866
4903
|
|
|
4867
4904
|
// src/index.ts
|
|
4868
|
-
var AIRCHAT_VERSION = true ? "1.
|
|
4905
|
+
var AIRCHAT_VERSION = true ? "1.1.0" : "dev";
|
|
4869
4906
|
var DEFAULT_AIRCHAT_URL2 = "https://supernode-web-production.up.railway.app";
|
|
4870
4907
|
var GREEN = "\x1B[32m";
|
|
4871
4908
|
var YELLOW = "\x1B[33m";
|
|
@@ -5091,7 +5128,9 @@ async function cloneAndInstall(config) {
|
|
|
5091
5128
|
}
|
|
5092
5129
|
try {
|
|
5093
5130
|
console.log(` Cloning airchat...`);
|
|
5094
|
-
|
|
5131
|
+
execFileSync2("git", ["clone", "https://github.com/prone/airchat.git", config.airchatDir], {
|
|
5132
|
+
stdio: "pipe"
|
|
5133
|
+
});
|
|
5095
5134
|
console.log(` Installing dependencies...`);
|
|
5096
5135
|
execSync2("npm install", { cwd: config.airchatDir, stdio: "pipe" });
|
|
5097
5136
|
return { name, ok: true, message: "Cloned and installed" };
|
|
@@ -5114,7 +5153,7 @@ async function runMigrations(config) {
|
|
|
5114
5153
|
}
|
|
5115
5154
|
try {
|
|
5116
5155
|
execSync2("which supabase", { stdio: "pipe" });
|
|
5117
|
-
|
|
5156
|
+
execFileSync2("supabase", ["db", "push", "--db-url", config.supabaseUrl], {
|
|
5118
5157
|
cwd: config.airchatDir,
|
|
5119
5158
|
stdio: "pipe"
|
|
5120
5159
|
});
|
|
@@ -5150,7 +5189,9 @@ async function runMigrations(config) {
|
|
|
5150
5189
|
const files = fs4.readdirSync(migrationsDir).filter((f) => f.endsWith(".sql")).sort();
|
|
5151
5190
|
try {
|
|
5152
5191
|
for (const file of files) {
|
|
5153
|
-
|
|
5192
|
+
execFileSync2("psql", [config.databaseUrl, "-f", path4.join(migrationsDir, file)], {
|
|
5193
|
+
stdio: "pipe"
|
|
5194
|
+
});
|
|
5154
5195
|
}
|
|
5155
5196
|
return { name, ok: true, message: `Applied ${files.length} migrations` };
|
|
5156
5197
|
} catch (e) {
|
|
@@ -5284,7 +5325,8 @@ function writeWebEnv(config) {
|
|
|
5284
5325
|
if (config.databaseUrl) {
|
|
5285
5326
|
lines.push(`DATABASE_URL=${config.databaseUrl}`);
|
|
5286
5327
|
}
|
|
5287
|
-
fs4.writeFileSync(envPath, lines.join("\n") + "\n");
|
|
5328
|
+
fs4.writeFileSync(envPath, lines.join("\n") + "\n", { mode: 384 });
|
|
5329
|
+
fs4.chmodSync(envPath, 384);
|
|
5288
5330
|
return { name, ok: true, message: envPath };
|
|
5289
5331
|
} catch (e) {
|
|
5290
5332
|
return { name, ok: false, message: e.message };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "airchat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
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.
|
|
31
|
+
"tsx": "^4.23.7",
|
|
32
32
|
"typescript": "^6.0.0"
|
|
33
33
|
}
|
|
34
34
|
}
|