@agentchatme/cli 0.0.135 → 0.0.137
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 +240 -161
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4554,6 +4554,9 @@ function formatStopPickup(handle, rows) {
|
|
|
4554
4554
|
"Handle these per your AgentChat skill, then finish. Reply via agentchat_send_message only where warranted \u2014 if nothing is actionable, simply end the turn (silence is a valid outcome)."
|
|
4555
4555
|
].join("\n");
|
|
4556
4556
|
}
|
|
4557
|
+
function formatAlwaysOnDown(platform) {
|
|
4558
|
+
return `\u26A0 Always-on is down \u2014 while you are away I won\u2019t be able to answer messages (they queue for your next session, nothing is lost). Turn it back on: \`agentchat daemon install --platform ${platform}\``;
|
|
4559
|
+
}
|
|
4557
4560
|
function formatRegistrationOffer(cliPath, platform) {
|
|
4558
4561
|
const invoke = cliPath ? `node "${cliPath}"` : "agentchat";
|
|
4559
4562
|
const p = platform ? ` --platform ${platform}` : "";
|
|
@@ -4579,6 +4582,140 @@ function formatRegistrationOffer(cliPath, platform) {
|
|
|
4579
4582
|
].join("\n");
|
|
4580
4583
|
}
|
|
4581
4584
|
|
|
4585
|
+
// src/commands/daemon.ts
|
|
4586
|
+
import * as fs3 from "fs";
|
|
4587
|
+
import * as os2 from "os";
|
|
4588
|
+
import * as path4 from "path";
|
|
4589
|
+
import { spawnSync } from "child_process";
|
|
4590
|
+
var DAEMON_PKG = "@agentchatme/daemon";
|
|
4591
|
+
function runtimeDir() {
|
|
4592
|
+
return path4.join(os2.homedir(), ".agentchat", "daemon-runtime");
|
|
4593
|
+
}
|
|
4594
|
+
function daemonEntry() {
|
|
4595
|
+
return path4.join(runtimeDir(), "node_modules", "@agentchatme", "daemon", "dist", "index.js");
|
|
4596
|
+
}
|
|
4597
|
+
var ALWAYS_ON_WANTED = "always-on.wanted";
|
|
4598
|
+
var HEARTBEAT_FILE = "daemon.heartbeat";
|
|
4599
|
+
var HEARTBEAT_STALE_MS = 3 * 6e4;
|
|
4600
|
+
function markAlwaysOnWanted(home) {
|
|
4601
|
+
try {
|
|
4602
|
+
fs3.mkdirSync(home, { recursive: true });
|
|
4603
|
+
fs3.writeFileSync(path4.join(home, ALWAYS_ON_WANTED), "");
|
|
4604
|
+
} catch {
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
function clearAlwaysOnWanted(home) {
|
|
4608
|
+
try {
|
|
4609
|
+
fs3.rmSync(path4.join(home, ALWAYS_ON_WANTED), { force: true });
|
|
4610
|
+
} catch {
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
function alwaysOnHealth(home) {
|
|
4614
|
+
if (!fs3.existsSync(path4.join(home, ALWAYS_ON_WANTED))) return { wanted: false, healthy: true };
|
|
4615
|
+
try {
|
|
4616
|
+
const age = Date.now() - fs3.statSync(path4.join(home, HEARTBEAT_FILE)).mtimeMs;
|
|
4617
|
+
return { wanted: true, healthy: age <= HEARTBEAT_STALE_MS };
|
|
4618
|
+
} catch {
|
|
4619
|
+
return { wanted: true, healthy: false };
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
function ensureDaemon() {
|
|
4623
|
+
if (fs3.existsSync(daemonEntry())) return { ok: true };
|
|
4624
|
+
const dir = runtimeDir();
|
|
4625
|
+
try {
|
|
4626
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
4627
|
+
} catch (err) {
|
|
4628
|
+
return { ok: false, detail: `could not create ${dir}: ${String(err)}` };
|
|
4629
|
+
}
|
|
4630
|
+
const r = spawnSync(
|
|
4631
|
+
"npm",
|
|
4632
|
+
["install", DAEMON_PKG, "--prefix", dir, "--no-save", "--no-audit", "--no-fund"],
|
|
4633
|
+
{ encoding: "utf-8", timeout: 18e4 }
|
|
4634
|
+
);
|
|
4635
|
+
if (r.error || r.status !== 0) {
|
|
4636
|
+
const why = (r.stderr || r.error && r.error.message || "unknown error").slice(0, 300);
|
|
4637
|
+
return { ok: false, detail: `npm install ${DAEMON_PKG} failed: ${why}` };
|
|
4638
|
+
}
|
|
4639
|
+
return fs3.existsSync(daemonEntry()) ? { ok: true } : { ok: false, detail: "installed but entrypoint missing" };
|
|
4640
|
+
}
|
|
4641
|
+
function runDaemon(args) {
|
|
4642
|
+
const r = spawnSync(process.execPath, [daemonEntry(), ...args], { stdio: "inherit" });
|
|
4643
|
+
return r.status ?? 1;
|
|
4644
|
+
}
|
|
4645
|
+
function tryInstallDaemon(platform, home) {
|
|
4646
|
+
const got = ensureDaemon();
|
|
4647
|
+
if (!got.ok) return { ok: false, detail: got.detail ?? "runtime fetch failed" };
|
|
4648
|
+
const r = spawnSync(
|
|
4649
|
+
process.execPath,
|
|
4650
|
+
[daemonEntry(), "install", "--runtime", platform, "--home", home],
|
|
4651
|
+
{ encoding: "utf-8", timeout: 12e4 }
|
|
4652
|
+
);
|
|
4653
|
+
if (r.error || r.status !== 0) {
|
|
4654
|
+
const raw = r.stderr || r.stdout || r.error?.message || "";
|
|
4655
|
+
return { ok: false, detail: raw.slice(0, 300).trim() || "service install failed" };
|
|
4656
|
+
}
|
|
4657
|
+
markAlwaysOnWanted(home);
|
|
4658
|
+
return { ok: true };
|
|
4659
|
+
}
|
|
4660
|
+
async function runDaemonCmd(sub, platform) {
|
|
4661
|
+
if (platform === "cursor") {
|
|
4662
|
+
console.error("The always-on daemon supports Claude Code and Codex (Cursor not yet).");
|
|
4663
|
+
return 1;
|
|
4664
|
+
}
|
|
4665
|
+
const runtime = platform;
|
|
4666
|
+
const bound = process.env["AGENTCHAT_HOME"]?.trim();
|
|
4667
|
+
const home = bound && bound.length > 0 ? bound : hostHome(platform);
|
|
4668
|
+
if (sub === "install") {
|
|
4669
|
+
const creds = readCredentialsFileAt(home);
|
|
4670
|
+
if (creds === null) {
|
|
4671
|
+
console.error(
|
|
4672
|
+
`No AgentChat identity for ${platform} yet. Register first, then run:
|
|
4673
|
+
agentchat daemon install --platform ${platform}`
|
|
4674
|
+
);
|
|
4675
|
+
return 1;
|
|
4676
|
+
}
|
|
4677
|
+
const got = ensureDaemon();
|
|
4678
|
+
if (!got.ok) {
|
|
4679
|
+
console.error(`Couldn't set up always-on automatically: ${got.detail}`);
|
|
4680
|
+
console.error(`Finish it by hand:
|
|
4681
|
+
npm i -g ${DAEMON_PKG}
|
|
4682
|
+
agentchatd install --runtime ${runtime}`);
|
|
4683
|
+
return 1;
|
|
4684
|
+
}
|
|
4685
|
+
const code = runDaemon(["install", "--runtime", runtime, "--home", home]);
|
|
4686
|
+
if (code === 0) {
|
|
4687
|
+
markAlwaysOnWanted(home);
|
|
4688
|
+
console.log(
|
|
4689
|
+
[
|
|
4690
|
+
"",
|
|
4691
|
+
`Always-on is ON for @${creds.handle} \u2014 it answers DMs even when you're not in a session, while this machine is up.`,
|
|
4692
|
+
`Want session-only instead? Turn it off any time: agentchat daemon disable --platform ${platform}`
|
|
4693
|
+
].join("\n")
|
|
4694
|
+
);
|
|
4695
|
+
}
|
|
4696
|
+
return code;
|
|
4697
|
+
}
|
|
4698
|
+
if (sub === "enable" || sub === "disable" || sub === "status" || sub === "uninstall") {
|
|
4699
|
+
if (!fs3.existsSync(daemonEntry())) {
|
|
4700
|
+
if (sub === "status") {
|
|
4701
|
+
console.log("Always-on: not installed (session-only). Turn it on with: agentchat daemon install");
|
|
4702
|
+
return 0;
|
|
4703
|
+
}
|
|
4704
|
+
if (sub === "disable") clearAlwaysOnWanted(home);
|
|
4705
|
+
console.error(`Always-on isn't set up yet. Turn it on with: agentchat daemon install --platform ${platform}`);
|
|
4706
|
+
return sub === "disable" ? 0 : 1;
|
|
4707
|
+
}
|
|
4708
|
+
const code = runDaemon([sub, "--runtime", runtime, "--home", home]);
|
|
4709
|
+
if (code === 0) {
|
|
4710
|
+
if (sub === "enable") markAlwaysOnWanted(home);
|
|
4711
|
+
else if (sub === "disable" || sub === "uninstall") clearAlwaysOnWanted(home);
|
|
4712
|
+
}
|
|
4713
|
+
return code;
|
|
4714
|
+
}
|
|
4715
|
+
console.error("Usage: agentchat daemon <install|enable|disable|status|uninstall> --platform <claude-code|codex>");
|
|
4716
|
+
return 1;
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4582
4719
|
// src/commands/hook.ts
|
|
4583
4720
|
var SESSION_START_PEEK_LIMIT = 100;
|
|
4584
4721
|
var STOP_PEEK_LIMIT = 50;
|
|
@@ -4622,7 +4759,7 @@ async function claimContiguousPrefix(cfg, rows, holder) {
|
|
|
4622
4759
|
async function runSessionStartHook(platform) {
|
|
4623
4760
|
try {
|
|
4624
4761
|
if (hooksDisabled()) return;
|
|
4625
|
-
bindHostHome(platform);
|
|
4762
|
+
const home = bindHostHome(platform);
|
|
4626
4763
|
const input = await readHookInput();
|
|
4627
4764
|
if (input.source === "compact") return;
|
|
4628
4765
|
resetSession(`${platform}:${input.sessionId}`);
|
|
@@ -4636,12 +4773,19 @@ async function runSessionStartHook(platform) {
|
|
|
4636
4773
|
}
|
|
4637
4774
|
const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
|
|
4638
4775
|
await markSessionActive(cfg);
|
|
4776
|
+
const h = alwaysOnHealth(home);
|
|
4777
|
+
const alert = h.wanted && !h.healthy ? formatAlwaysOnDown(platform) : null;
|
|
4639
4778
|
const peeked = ackableRows(await syncPeek(cfg, { limit: SESSION_START_PEEK_LIMIT }));
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4779
|
+
const rows = peeked.length > 0 ? await claimContiguousPrefix(cfg, peeked, `session:${input.sessionId}`) : [];
|
|
4780
|
+
if (rows.length === 0) {
|
|
4781
|
+
if (alert !== null) printJson(sessionStartOutput(platform, alert));
|
|
4782
|
+
return;
|
|
4783
|
+
}
|
|
4643
4784
|
const handle = await resolveHandle(cfg, identity.handle);
|
|
4644
|
-
const
|
|
4785
|
+
const digest = formatSessionStart(handle, rows);
|
|
4786
|
+
const context = alert !== null ? `${alert}
|
|
4787
|
+
|
|
4788
|
+
${digest}` : digest;
|
|
4645
4789
|
const cursor = lastDeliveryId(rows);
|
|
4646
4790
|
if (cursor !== null) {
|
|
4647
4791
|
setPendingAck(`${platform}:${input.sessionId}`, cursor);
|
|
@@ -4709,8 +4853,8 @@ async function runStopHook(platform) {
|
|
|
4709
4853
|
}
|
|
4710
4854
|
|
|
4711
4855
|
// src/commands/identity.ts
|
|
4712
|
-
import * as
|
|
4713
|
-
import * as
|
|
4856
|
+
import * as fs6 from "fs";
|
|
4857
|
+
import * as path7 from "path";
|
|
4714
4858
|
import * as readline from "readline/promises";
|
|
4715
4859
|
|
|
4716
4860
|
// ../node_modules/.pnpm/agentchatme@1.0.2_ws@8.21.1/node_modules/agentchatme/dist/index.js
|
|
@@ -5979,9 +6123,9 @@ var AgentChatClient = class _AgentChatClient {
|
|
|
5979
6123
|
var MAX_ATTACHMENT_SIZE = 25 * 1024 * 1024;
|
|
5980
6124
|
|
|
5981
6125
|
// src/lib/anchor.ts
|
|
5982
|
-
import * as
|
|
5983
|
-
import * as
|
|
5984
|
-
import * as
|
|
6126
|
+
import * as fs4 from "fs";
|
|
6127
|
+
import * as os3 from "os";
|
|
6128
|
+
import * as path5 from "path";
|
|
5985
6129
|
var ANCHOR_START = "<!-- agentchat:start -->";
|
|
5986
6130
|
var ANCHOR_END = "<!-- agentchat:end -->";
|
|
5987
6131
|
var LEGACY_ANCHOR_START = "<!-- agentchat-skill:start -->";
|
|
@@ -5989,9 +6133,9 @@ var LEGACY_ANCHOR_END = "<!-- agentchat-skill:end -->";
|
|
|
5989
6133
|
function anchorFilePath(platform) {
|
|
5990
6134
|
switch (platform) {
|
|
5991
6135
|
case "claude-code":
|
|
5992
|
-
return
|
|
6136
|
+
return path5.join(os3.homedir(), ".claude", "CLAUDE.md");
|
|
5993
6137
|
case "codex":
|
|
5994
|
-
return
|
|
6138
|
+
return path5.join(codexHome(), "AGENTS.md");
|
|
5995
6139
|
case "cursor":
|
|
5996
6140
|
return null;
|
|
5997
6141
|
}
|
|
@@ -6016,11 +6160,11 @@ function installAnchor(platform, handle) {
|
|
|
6016
6160
|
if (filePath === null) return { platform, path: null, action: "unsupported" };
|
|
6017
6161
|
const trimmedHandle = handle.trim();
|
|
6018
6162
|
if (!trimmedHandle) throw new Error("installAnchor: handle is empty");
|
|
6019
|
-
|
|
6020
|
-
const existing =
|
|
6163
|
+
fs4.mkdirSync(path5.dirname(filePath), { recursive: true });
|
|
6164
|
+
const existing = fs4.existsSync(filePath) ? fs4.readFileSync(filePath, "utf-8") : "";
|
|
6021
6165
|
const next = upsertAnchorBlock(existing, renderAnchorBlock(trimmedHandle));
|
|
6022
|
-
|
|
6023
|
-
const verify =
|
|
6166
|
+
fs4.writeFileSync(filePath, next, "utf-8");
|
|
6167
|
+
const verify = fs4.readFileSync(filePath, "utf-8");
|
|
6024
6168
|
if (!verify.includes(`@${trimmedHandle}`)) {
|
|
6025
6169
|
throw new Error(
|
|
6026
6170
|
`installAnchor: handle @${trimmedHandle} did not land in ${filePath} \u2014 please remove the agentchat block manually and re-run.`
|
|
@@ -6031,17 +6175,17 @@ function installAnchor(platform, handle) {
|
|
|
6031
6175
|
function removeAnchor(platform) {
|
|
6032
6176
|
const filePath = anchorFilePath(platform);
|
|
6033
6177
|
if (filePath === null) return { platform, path: null, action: "unsupported" };
|
|
6034
|
-
if (!
|
|
6035
|
-
const existing =
|
|
6178
|
+
if (!fs4.existsSync(filePath)) return { platform, path: filePath, action: "noop" };
|
|
6179
|
+
const existing = fs4.readFileSync(filePath, "utf-8");
|
|
6036
6180
|
const next = stripAnchorBlock(existing);
|
|
6037
6181
|
if (next === existing) return { platform, path: filePath, action: "noop" };
|
|
6038
|
-
|
|
6182
|
+
fs4.writeFileSync(filePath, next, "utf-8");
|
|
6039
6183
|
return { platform, path: filePath, action: "removed" };
|
|
6040
6184
|
}
|
|
6041
6185
|
function hasAnchor(platform) {
|
|
6042
6186
|
const filePath = anchorFilePath(platform);
|
|
6043
|
-
if (filePath === null || !
|
|
6044
|
-
const content =
|
|
6187
|
+
if (filePath === null || !fs4.existsSync(filePath)) return false;
|
|
6188
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
6045
6189
|
return findBlock(content, ANCHOR_START, ANCHOR_END) !== null;
|
|
6046
6190
|
}
|
|
6047
6191
|
function lineAnchoredIndex(text, marker, fromIndex = 0) {
|
|
@@ -6090,22 +6234,22 @@ function stripAllBlocks(existing, start, end) {
|
|
|
6090
6234
|
}
|
|
6091
6235
|
|
|
6092
6236
|
// src/lib/codex-config.ts
|
|
6093
|
-
import * as
|
|
6094
|
-
import * as
|
|
6237
|
+
import * as fs5 from "fs";
|
|
6238
|
+
import * as path6 from "path";
|
|
6095
6239
|
var TOML_START = "# agentchat:start";
|
|
6096
6240
|
var TOML_END = "# agentchat:end";
|
|
6097
|
-
var BUNDLE_REL =
|
|
6241
|
+
var BUNDLE_REL = path6.join("bin", "agentchat.mjs");
|
|
6098
6242
|
function codexConfigPath() {
|
|
6099
|
-
return
|
|
6243
|
+
return path6.join(codexHome(), "config.toml");
|
|
6100
6244
|
}
|
|
6101
6245
|
function codexHooksPath() {
|
|
6102
|
-
return
|
|
6246
|
+
return path6.join(codexHome(), "hooks.json");
|
|
6103
6247
|
}
|
|
6104
6248
|
function codexIdentityHome() {
|
|
6105
6249
|
return hostHome("codex");
|
|
6106
6250
|
}
|
|
6107
6251
|
function stableBundlePath() {
|
|
6108
|
-
return
|
|
6252
|
+
return path6.join(codexIdentityHome(), BUNDLE_REL);
|
|
6109
6253
|
}
|
|
6110
6254
|
function renderCodexAgents(handle) {
|
|
6111
6255
|
return [
|
|
@@ -6219,17 +6363,17 @@ function unmergeHooks(existing) {
|
|
|
6219
6363
|
}
|
|
6220
6364
|
function copyBundle(bundleSrc) {
|
|
6221
6365
|
const dest = stableBundlePath();
|
|
6222
|
-
|
|
6223
|
-
const srcResolved =
|
|
6224
|
-
if (srcResolved !==
|
|
6225
|
-
|
|
6366
|
+
fs5.mkdirSync(path6.dirname(dest), { recursive: true });
|
|
6367
|
+
const srcResolved = path6.resolve(bundleSrc);
|
|
6368
|
+
if (srcResolved !== path6.resolve(dest)) {
|
|
6369
|
+
fs5.copyFileSync(srcResolved, dest);
|
|
6226
6370
|
}
|
|
6227
6371
|
return dest;
|
|
6228
6372
|
}
|
|
6229
6373
|
function installCodex(bundleSrc, handle) {
|
|
6230
6374
|
const actions = [];
|
|
6231
6375
|
const warnings = [];
|
|
6232
|
-
|
|
6376
|
+
fs5.mkdirSync(codexHome(), { recursive: true });
|
|
6233
6377
|
let bundle;
|
|
6234
6378
|
try {
|
|
6235
6379
|
bundle = copyBundle(bundleSrc);
|
|
@@ -6241,36 +6385,36 @@ function installCodex(bundleSrc, handle) {
|
|
|
6241
6385
|
);
|
|
6242
6386
|
}
|
|
6243
6387
|
const cfgPath = codexConfigPath();
|
|
6244
|
-
const existingCfg =
|
|
6388
|
+
const existingCfg = fs5.existsSync(cfgPath) ? fs5.readFileSync(cfgPath, "utf-8") : "";
|
|
6245
6389
|
if (hasUnfencedAgentchatServer(existingCfg)) {
|
|
6246
6390
|
warnings.push(
|
|
6247
6391
|
`${cfgPath} already defines [mcp_servers.agentchat] outside our block \u2014 left it untouched; remove it and re-run if it isn't ours`
|
|
6248
6392
|
);
|
|
6249
6393
|
} else {
|
|
6250
|
-
|
|
6394
|
+
fs5.writeFileSync(cfgPath, upsertTomlBlock(existingCfg, mcpBlock()), "utf-8");
|
|
6251
6395
|
actions.push(`config.toml \u2190 [mcp_servers.agentchat]`);
|
|
6252
6396
|
}
|
|
6253
6397
|
const hooksPath = codexHooksPath();
|
|
6254
6398
|
let existingHooks = null;
|
|
6255
|
-
if (
|
|
6399
|
+
if (fs5.existsSync(hooksPath)) {
|
|
6256
6400
|
try {
|
|
6257
|
-
existingHooks = JSON.parse(
|
|
6401
|
+
existingHooks = JSON.parse(fs5.readFileSync(hooksPath, "utf-8"));
|
|
6258
6402
|
} catch {
|
|
6259
6403
|
warnings.push(`${hooksPath} was not valid JSON \u2014 leaving it; wire hooks manually if needed`);
|
|
6260
6404
|
}
|
|
6261
6405
|
}
|
|
6262
|
-
if (existingHooks !== null || !
|
|
6406
|
+
if (existingHooks !== null || !fs5.existsSync(hooksPath)) {
|
|
6263
6407
|
const merged = mergeHooks(existingHooks, bundle);
|
|
6264
|
-
|
|
6408
|
+
fs5.writeFileSync(hooksPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
6265
6409
|
actions.push("hooks.json \u2190 SessionStart + Stop + UserPromptSubmit");
|
|
6266
6410
|
}
|
|
6267
6411
|
if (handle) {
|
|
6268
6412
|
try {
|
|
6269
|
-
const agentsPath =
|
|
6270
|
-
const existing =
|
|
6413
|
+
const agentsPath = path6.join(codexHome(), "AGENTS.md");
|
|
6414
|
+
const existing = fs5.existsSync(agentsPath) ? fs5.readFileSync(agentsPath, "utf-8") : "";
|
|
6271
6415
|
const next = upsertAnchorBlock(existing, renderCodexAgents(handle));
|
|
6272
|
-
|
|
6273
|
-
if (!
|
|
6416
|
+
fs5.writeFileSync(agentsPath, next, "utf-8");
|
|
6417
|
+
if (!fs5.readFileSync(agentsPath, "utf-8").includes(`@${handle}`)) {
|
|
6274
6418
|
throw new Error("handle did not land in AGENTS.md");
|
|
6275
6419
|
}
|
|
6276
6420
|
actions.push(`AGENTS.md \u2190 identity + etiquette (@${handle})`);
|
|
@@ -6286,17 +6430,17 @@ function installCodex(bundleSrc, handle) {
|
|
|
6286
6430
|
function removeCodex() {
|
|
6287
6431
|
const removed = [];
|
|
6288
6432
|
const cfgPath = codexConfigPath();
|
|
6289
|
-
if (
|
|
6290
|
-
const stripped = stripTomlBlock(
|
|
6291
|
-
|
|
6433
|
+
if (fs5.existsSync(cfgPath)) {
|
|
6434
|
+
const stripped = stripTomlBlock(fs5.readFileSync(cfgPath, "utf-8"));
|
|
6435
|
+
fs5.writeFileSync(cfgPath, stripped, "utf-8");
|
|
6292
6436
|
removed.push("config.toml [mcp_servers.agentchat]");
|
|
6293
6437
|
}
|
|
6294
6438
|
const hooksPath = codexHooksPath();
|
|
6295
|
-
if (
|
|
6439
|
+
if (fs5.existsSync(hooksPath)) {
|
|
6296
6440
|
try {
|
|
6297
|
-
const next = unmergeHooks(JSON.parse(
|
|
6298
|
-
if (next === null)
|
|
6299
|
-
else
|
|
6441
|
+
const next = unmergeHooks(JSON.parse(fs5.readFileSync(hooksPath, "utf-8")));
|
|
6442
|
+
if (next === null) fs5.unlinkSync(hooksPath);
|
|
6443
|
+
else fs5.writeFileSync(hooksPath, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
6300
6444
|
removed.push("hooks.json entries");
|
|
6301
6445
|
} catch {
|
|
6302
6446
|
}
|
|
@@ -6344,7 +6488,7 @@ var RESTART_HINT = "Your messaging tools pick this up immediately \u2014 no rest
|
|
|
6344
6488
|
function autoAnchor(handle) {
|
|
6345
6489
|
const lines = [];
|
|
6346
6490
|
const ccFile = anchorFilePath("claude-code");
|
|
6347
|
-
if (ccFile !== null &&
|
|
6491
|
+
if (ccFile !== null && fs6.existsSync(path7.dirname(ccFile))) {
|
|
6348
6492
|
try {
|
|
6349
6493
|
installAnchor("claude-code", handle);
|
|
6350
6494
|
lines.push(` anchor claude-code: written \u2192 ${ccFile}`);
|
|
@@ -6353,10 +6497,10 @@ function autoAnchor(handle) {
|
|
|
6353
6497
|
}
|
|
6354
6498
|
}
|
|
6355
6499
|
const codexAgents = anchorFilePath("codex");
|
|
6356
|
-
if (codexAgents !== null &&
|
|
6500
|
+
if (codexAgents !== null && fs6.existsSync(codexAgents)) {
|
|
6357
6501
|
try {
|
|
6358
|
-
const existing =
|
|
6359
|
-
|
|
6502
|
+
const existing = fs6.readFileSync(codexAgents, "utf-8");
|
|
6503
|
+
fs6.writeFileSync(codexAgents, upsertAnchorBlock(existing, renderCodexAgents(handle)), "utf-8");
|
|
6360
6504
|
lines.push(` AGENTS.md codex: refreshed \u2192 ${codexAgents}`);
|
|
6361
6505
|
} catch (err) {
|
|
6362
6506
|
lines.push(` AGENTS.md codex: FAILED \u2014 ${String(err)}`);
|
|
@@ -6364,6 +6508,24 @@ function autoAnchor(handle) {
|
|
|
6364
6508
|
}
|
|
6365
6509
|
return lines;
|
|
6366
6510
|
}
|
|
6511
|
+
function autoDaemon(platform) {
|
|
6512
|
+
if (platform === void 0 || platform === "cursor") {
|
|
6513
|
+
return [
|
|
6514
|
+
"Next, turn on always-on so you're reachable when the user is away: `agentchat daemon install` (on by default \u2014 `agentchat daemon disable` for session-only)."
|
|
6515
|
+
];
|
|
6516
|
+
}
|
|
6517
|
+
const home = process.env["AGENTCHAT_HOME"]?.trim() || hostHome(platform);
|
|
6518
|
+
process.stderr.write("Setting up always-on (one-time)\u2026\n");
|
|
6519
|
+
const res = tryInstallDaemon(platform, home);
|
|
6520
|
+
if (res.ok) {
|
|
6521
|
+
return [
|
|
6522
|
+
`Always-on is ON \u2014 you'll answer DMs even when the user isn't in a session (while this machine is up). Prefer session-only? \`agentchat daemon disable --platform ${platform}\`.`
|
|
6523
|
+
];
|
|
6524
|
+
}
|
|
6525
|
+
return [
|
|
6526
|
+
`(Always-on didn't auto-start: ${res.detail.split("\n")[0]}) Turn it on when ready: \`agentchat daemon install --platform ${platform}\`.`
|
|
6527
|
+
];
|
|
6528
|
+
}
|
|
6367
6529
|
async function runRegister(opts) {
|
|
6368
6530
|
const apiBase = opts.apiBase ?? process.env["AGENTCHAT_API_BASE"] ?? DEFAULT_API_BASE;
|
|
6369
6531
|
if (opts.code !== void 0) {
|
|
@@ -6407,7 +6569,7 @@ async function runRegister(opts) {
|
|
|
6407
6569
|
"",
|
|
6408
6570
|
"This identity is scoped to this coding agent \u2014 each coding agent on the machine gets its own handle.",
|
|
6409
6571
|
`Other agents can DM you at @${pendingHandle}. Check \`agentchat status\` any time.`,
|
|
6410
|
-
|
|
6572
|
+
...autoDaemon(opts.platform),
|
|
6411
6573
|
RESTART_HINT
|
|
6412
6574
|
].join("\n")
|
|
6413
6575
|
);
|
|
@@ -6513,7 +6675,7 @@ async function runLogin(opts) {
|
|
|
6513
6675
|
[
|
|
6514
6676
|
`Signed in as @${me.handle}.`,
|
|
6515
6677
|
...anchorReport,
|
|
6516
|
-
|
|
6678
|
+
...autoDaemon(opts.platform),
|
|
6517
6679
|
RESTART_HINT
|
|
6518
6680
|
].join("\n")
|
|
6519
6681
|
);
|
|
@@ -6552,6 +6714,7 @@ async function runRecover(opts) {
|
|
|
6552
6714
|
[
|
|
6553
6715
|
`Recovered: @${result.handle} \u2014 a fresh API key is stored (the old key is now revoked).`,
|
|
6554
6716
|
...anchorReport,
|
|
6717
|
+
...autoDaemon(opts.platform),
|
|
6555
6718
|
RESTART_HINT
|
|
6556
6719
|
].join("\n")
|
|
6557
6720
|
);
|
|
@@ -6775,10 +6938,10 @@ function runLogout() {
|
|
|
6775
6938
|
}
|
|
6776
6939
|
|
|
6777
6940
|
// src/commands/install.ts
|
|
6778
|
-
import * as
|
|
6779
|
-
import * as
|
|
6780
|
-
import * as
|
|
6781
|
-
import { spawnSync } from "child_process";
|
|
6941
|
+
import * as fs7 from "fs";
|
|
6942
|
+
import * as os4 from "os";
|
|
6943
|
+
import * as path8 from "path";
|
|
6944
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
6782
6945
|
var MARKETPLACE_SLUG = "agentchatme/agentchat-coding-agents";
|
|
6783
6946
|
var PLUGIN_REF = "agentchat@agentchatme";
|
|
6784
6947
|
var PROBES = [
|
|
@@ -6787,18 +6950,18 @@ var PROBES = [
|
|
|
6787
6950
|
{ key: "cursor", label: "Cursor", binary: "cursor-agent", configDir: ".cursor" }
|
|
6788
6951
|
];
|
|
6789
6952
|
function defaultRun(cmd, args) {
|
|
6790
|
-
const result =
|
|
6953
|
+
const result = spawnSync2(cmd, args, { stdio: ["ignore", "pipe", "pipe"], timeout: 6e4 });
|
|
6791
6954
|
if (result.error) return null;
|
|
6792
6955
|
return result.status;
|
|
6793
6956
|
}
|
|
6794
6957
|
function binaryOnPath(binary, env) {
|
|
6795
6958
|
const pathVar = env["PATH"] ?? "";
|
|
6796
6959
|
const exts = process.platform === "win32" ? [".cmd", ".exe", ".bat", ""] : [""];
|
|
6797
|
-
for (const dir of pathVar.split(
|
|
6960
|
+
for (const dir of pathVar.split(path8.delimiter)) {
|
|
6798
6961
|
if (dir.length === 0) continue;
|
|
6799
6962
|
for (const ext of exts) {
|
|
6800
6963
|
try {
|
|
6801
|
-
if (
|
|
6964
|
+
if (fs7.existsSync(path8.join(dir, binary + ext))) return true;
|
|
6802
6965
|
} catch {
|
|
6803
6966
|
}
|
|
6804
6967
|
}
|
|
@@ -6807,13 +6970,13 @@ function binaryOnPath(binary, env) {
|
|
|
6807
6970
|
}
|
|
6808
6971
|
function detectPlatforms(env, home) {
|
|
6809
6972
|
return PROBES.filter(
|
|
6810
|
-
(p) => binaryOnPath(p.binary, env) ||
|
|
6973
|
+
(p) => binaryOnPath(p.binary, env) || fs7.existsSync(path8.join(home, p.configDir))
|
|
6811
6974
|
);
|
|
6812
6975
|
}
|
|
6813
6976
|
async function runInstall(deps = {}) {
|
|
6814
6977
|
const run = deps.run ?? defaultRun;
|
|
6815
6978
|
const env = deps.env ?? process.env;
|
|
6816
|
-
const home = deps.homedir ??
|
|
6979
|
+
const home = deps.homedir ?? os4.homedir();
|
|
6817
6980
|
const detected = detectPlatforms(env, home);
|
|
6818
6981
|
if (detected.length === 0) {
|
|
6819
6982
|
console.log(
|
|
@@ -6890,11 +7053,11 @@ Signed in: ${have.join(", ")}`);
|
|
|
6890
7053
|
}
|
|
6891
7054
|
|
|
6892
7055
|
// src/commands/doctor.ts
|
|
6893
|
-
import * as
|
|
6894
|
-
import * as
|
|
7056
|
+
import * as fs8 from "fs";
|
|
7057
|
+
import * as path9 from "path";
|
|
6895
7058
|
|
|
6896
7059
|
// src/version.ts
|
|
6897
|
-
var VERSION2 = "0.0.
|
|
7060
|
+
var VERSION2 = "0.0.137";
|
|
6898
7061
|
|
|
6899
7062
|
// src/commands/doctor.ts
|
|
6900
7063
|
function fmt(check) {
|
|
@@ -6960,8 +7123,8 @@ async function runDoctor() {
|
|
|
6960
7123
|
for (const platform of ["claude-code", "codex"]) {
|
|
6961
7124
|
const file = anchorFilePath(platform);
|
|
6962
7125
|
if (file === null) continue;
|
|
6963
|
-
const hostDir =
|
|
6964
|
-
if (!
|
|
7126
|
+
const hostDir = path9.dirname(file);
|
|
7127
|
+
if (!fs8.existsSync(hostDir)) {
|
|
6965
7128
|
checks.push({ name: `anchor-${platform}`, verdict: "PASS", detail: `${hostDir} absent (host not installed) \u2014 skipped` });
|
|
6966
7129
|
} else {
|
|
6967
7130
|
checks.push({
|
|
@@ -6972,8 +7135,8 @@ async function runDoctor() {
|
|
|
6972
7135
|
}
|
|
6973
7136
|
}
|
|
6974
7137
|
try {
|
|
6975
|
-
|
|
6976
|
-
|
|
7138
|
+
fs8.mkdirSync(agentchatHome(), { recursive: true });
|
|
7139
|
+
fs8.accessSync(agentchatHome(), fs8.constants.W_OK);
|
|
6977
7140
|
checks.push({ name: "state", verdict: "PASS", detail: `${statePath()} writable` });
|
|
6978
7141
|
} catch {
|
|
6979
7142
|
checks.push({ name: "state", verdict: "FAIL", detail: `${agentchatHome()} is not writable` });
|
|
@@ -7006,93 +7169,6 @@ async function runAnchor(action, platform) {
|
|
|
7006
7169
|
return 0;
|
|
7007
7170
|
}
|
|
7008
7171
|
|
|
7009
|
-
// src/commands/daemon.ts
|
|
7010
|
-
import * as fs8 from "fs";
|
|
7011
|
-
import * as os4 from "os";
|
|
7012
|
-
import * as path9 from "path";
|
|
7013
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
7014
|
-
var DAEMON_PKG = "@agentchatme/daemon";
|
|
7015
|
-
function runtimeDir() {
|
|
7016
|
-
return path9.join(os4.homedir(), ".agentchat", "daemon-runtime");
|
|
7017
|
-
}
|
|
7018
|
-
function daemonEntry() {
|
|
7019
|
-
return path9.join(runtimeDir(), "node_modules", "@agentchatme", "daemon", "dist", "index.js");
|
|
7020
|
-
}
|
|
7021
|
-
function ensureDaemon() {
|
|
7022
|
-
if (fs8.existsSync(daemonEntry())) return { ok: true };
|
|
7023
|
-
const dir = runtimeDir();
|
|
7024
|
-
try {
|
|
7025
|
-
fs8.mkdirSync(dir, { recursive: true });
|
|
7026
|
-
} catch (err) {
|
|
7027
|
-
return { ok: false, detail: `could not create ${dir}: ${String(err)}` };
|
|
7028
|
-
}
|
|
7029
|
-
const r = spawnSync2(
|
|
7030
|
-
"npm",
|
|
7031
|
-
["install", DAEMON_PKG, "--prefix", dir, "--no-save", "--no-audit", "--no-fund"],
|
|
7032
|
-
{ encoding: "utf-8", timeout: 18e4 }
|
|
7033
|
-
);
|
|
7034
|
-
if (r.error || r.status !== 0) {
|
|
7035
|
-
const why = (r.stderr || r.error && r.error.message || "unknown error").slice(0, 300);
|
|
7036
|
-
return { ok: false, detail: `npm install ${DAEMON_PKG} failed: ${why}` };
|
|
7037
|
-
}
|
|
7038
|
-
return fs8.existsSync(daemonEntry()) ? { ok: true } : { ok: false, detail: "installed but entrypoint missing" };
|
|
7039
|
-
}
|
|
7040
|
-
function runDaemon(args) {
|
|
7041
|
-
const r = spawnSync2(process.execPath, [daemonEntry(), ...args], { stdio: "inherit" });
|
|
7042
|
-
return r.status ?? 1;
|
|
7043
|
-
}
|
|
7044
|
-
async function runDaemonCmd(sub, platform) {
|
|
7045
|
-
if (platform === "cursor") {
|
|
7046
|
-
console.error("The always-on daemon supports Claude Code and Codex (Cursor not yet).");
|
|
7047
|
-
return 1;
|
|
7048
|
-
}
|
|
7049
|
-
const runtime = platform;
|
|
7050
|
-
const bound = process.env["AGENTCHAT_HOME"]?.trim();
|
|
7051
|
-
const home = bound && bound.length > 0 ? bound : hostHome(platform);
|
|
7052
|
-
if (sub === "install") {
|
|
7053
|
-
const creds = readCredentialsFileAt(home);
|
|
7054
|
-
if (creds === null) {
|
|
7055
|
-
console.error(
|
|
7056
|
-
`No AgentChat identity for ${platform} yet. Register first, then run:
|
|
7057
|
-
agentchat daemon install --platform ${platform}`
|
|
7058
|
-
);
|
|
7059
|
-
return 1;
|
|
7060
|
-
}
|
|
7061
|
-
const got = ensureDaemon();
|
|
7062
|
-
if (!got.ok) {
|
|
7063
|
-
console.error(`Couldn't set up always-on automatically: ${got.detail}`);
|
|
7064
|
-
console.error(`Finish it by hand:
|
|
7065
|
-
npm i -g ${DAEMON_PKG}
|
|
7066
|
-
agentchatd install --runtime ${runtime}`);
|
|
7067
|
-
return 1;
|
|
7068
|
-
}
|
|
7069
|
-
const code = runDaemon(["install", "--runtime", runtime, "--home", home]);
|
|
7070
|
-
if (code === 0) {
|
|
7071
|
-
console.log(
|
|
7072
|
-
[
|
|
7073
|
-
"",
|
|
7074
|
-
`Always-on is ON for @${creds.handle} \u2014 it answers DMs even when you're not in a session, while this machine is up.`,
|
|
7075
|
-
`Want session-only instead? Turn it off any time: agentchat daemon disable --platform ${platform}`
|
|
7076
|
-
].join("\n")
|
|
7077
|
-
);
|
|
7078
|
-
}
|
|
7079
|
-
return code;
|
|
7080
|
-
}
|
|
7081
|
-
if (sub === "enable" || sub === "disable" || sub === "status" || sub === "uninstall") {
|
|
7082
|
-
if (!fs8.existsSync(daemonEntry())) {
|
|
7083
|
-
if (sub === "status") {
|
|
7084
|
-
console.log("Always-on: not installed (session-only). Turn it on with: agentchat daemon install");
|
|
7085
|
-
return 0;
|
|
7086
|
-
}
|
|
7087
|
-
console.error(`Always-on isn't set up yet. Turn it on with: agentchat daemon install --platform ${platform}`);
|
|
7088
|
-
return sub === "disable" ? 0 : 1;
|
|
7089
|
-
}
|
|
7090
|
-
return runDaemon([sub, "--runtime", runtime, "--home", home]);
|
|
7091
|
-
}
|
|
7092
|
-
console.error("Usage: agentchat daemon <install|enable|disable|status|uninstall> --platform <claude-code|codex>");
|
|
7093
|
-
return 1;
|
|
7094
|
-
}
|
|
7095
|
-
|
|
7096
7172
|
// src/index.ts
|
|
7097
7173
|
var USAGE = `agentchat ${VERSION2} \u2014 AgentChat companion CLI for coding agents
|
|
7098
7174
|
|
|
@@ -7163,18 +7239,21 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
7163
7239
|
...values["display-name"] !== void 0 ? { displayName: values["display-name"] } : {},
|
|
7164
7240
|
...values.description !== void 0 ? { description: values.description } : {},
|
|
7165
7241
|
...values.code !== void 0 ? { code: values.code } : {},
|
|
7166
|
-
...values["api-base"] !== void 0 ? { apiBase: values["api-base"] } : {}
|
|
7242
|
+
...values["api-base"] !== void 0 ? { apiBase: values["api-base"] } : {},
|
|
7243
|
+
...values.platform !== void 0 && isPlatform(values.platform) ? { platform: values.platform } : {}
|
|
7167
7244
|
});
|
|
7168
7245
|
case "login":
|
|
7169
7246
|
return runLogin({
|
|
7170
7247
|
...values["api-key"] !== void 0 ? { apiKey: values["api-key"] } : {},
|
|
7171
|
-
...values["api-base"] !== void 0 ? { apiBase: values["api-base"] } : {}
|
|
7248
|
+
...values["api-base"] !== void 0 ? { apiBase: values["api-base"] } : {},
|
|
7249
|
+
...values.platform !== void 0 && isPlatform(values.platform) ? { platform: values.platform } : {}
|
|
7172
7250
|
});
|
|
7173
7251
|
case "recover":
|
|
7174
7252
|
return runRecover({
|
|
7175
7253
|
...values.email !== void 0 ? { email: values.email } : {},
|
|
7176
7254
|
...values.code !== void 0 ? { code: values.code } : {},
|
|
7177
|
-
...values["api-base"] !== void 0 ? { apiBase: values["api-base"] } : {}
|
|
7255
|
+
...values["api-base"] !== void 0 ? { apiBase: values["api-base"] } : {},
|
|
7256
|
+
...values.platform !== void 0 && isPlatform(values.platform) ? { platform: values.platform } : {}
|
|
7178
7257
|
});
|
|
7179
7258
|
case "status":
|
|
7180
7259
|
return runStatus({ ...values.json !== void 0 ? { json: values.json } : {} });
|