@integrity-labs/agt-cli 0.26.2-eng5706.1 → 0.26.2
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/bin/agt.js +603 -180
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-LBYU24PW.js → chunk-4CESBZPM.js} +21 -7
- package/dist/{chunk-LBYU24PW.js.map → chunk-4CESBZPM.js.map} +1 -1
- package/dist/lib/manager-worker.js +2 -2
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +92 -0
- package/dist/mcp/slack-channel.js +15 -12
- package/dist/mcp/telegram-channel.js +130 -40
- package/package.json +1 -1
|
@@ -14299,10 +14299,82 @@ function isMode(value) {
|
|
|
14299
14299
|
return value === "thinking" || value === "working" || value === "waiting";
|
|
14300
14300
|
}
|
|
14301
14301
|
|
|
14302
|
+
// src/mcp-spawn-lock.ts
|
|
14303
|
+
import {
|
|
14304
|
+
existsSync,
|
|
14305
|
+
mkdirSync,
|
|
14306
|
+
readFileSync,
|
|
14307
|
+
renameSync,
|
|
14308
|
+
unlinkSync,
|
|
14309
|
+
writeFileSync
|
|
14310
|
+
} from "fs";
|
|
14311
|
+
import { join } from "path";
|
|
14312
|
+
function defaultIsPidAlive(pid) {
|
|
14313
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
14314
|
+
try {
|
|
14315
|
+
process.kill(pid, 0);
|
|
14316
|
+
return true;
|
|
14317
|
+
} catch (err) {
|
|
14318
|
+
const code = err.code;
|
|
14319
|
+
if (code === "ESRCH") return false;
|
|
14320
|
+
return true;
|
|
14321
|
+
}
|
|
14322
|
+
}
|
|
14323
|
+
function acquireMcpSpawnLock(args) {
|
|
14324
|
+
const { agentDir, basename, options = {} } = args;
|
|
14325
|
+
if (!agentDir) return { kind: "no-agent-dir" };
|
|
14326
|
+
const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
|
|
14327
|
+
const selfPid = options.selfPid ?? process.pid;
|
|
14328
|
+
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
14329
|
+
const path = join(agentDir, basename);
|
|
14330
|
+
const existing = readLockHolder(path);
|
|
14331
|
+
if (existing) {
|
|
14332
|
+
if (existing.pid === selfPid) {
|
|
14333
|
+
return { kind: "acquired", path };
|
|
14334
|
+
}
|
|
14335
|
+
if (isPidAlive(existing.pid)) {
|
|
14336
|
+
return { kind: "blocked", path, holder: existing };
|
|
14337
|
+
}
|
|
14338
|
+
}
|
|
14339
|
+
mkdirSync(agentDir, { recursive: true, mode: 448 });
|
|
14340
|
+
const tmpPath = `${path}.${selfPid}.tmp`;
|
|
14341
|
+
const payload = { pid: selfPid, started_at: now() };
|
|
14342
|
+
writeFileSync(tmpPath, JSON.stringify(payload), { mode: 384 });
|
|
14343
|
+
renameSync(tmpPath, path);
|
|
14344
|
+
return { kind: "acquired", path };
|
|
14345
|
+
}
|
|
14346
|
+
function releaseMcpSpawnLock(lockPath, opts = {}) {
|
|
14347
|
+
if (!lockPath) return;
|
|
14348
|
+
const selfPid = opts.selfPid ?? process.pid;
|
|
14349
|
+
const existing = readLockHolder(lockPath);
|
|
14350
|
+
if (!existing) return;
|
|
14351
|
+
if (existing.pid !== selfPid) return;
|
|
14352
|
+
try {
|
|
14353
|
+
unlinkSync(lockPath);
|
|
14354
|
+
} catch {
|
|
14355
|
+
}
|
|
14356
|
+
}
|
|
14357
|
+
function readLockHolder(path) {
|
|
14358
|
+
if (!existsSync(path)) return null;
|
|
14359
|
+
try {
|
|
14360
|
+
const raw = readFileSync(path, "utf8");
|
|
14361
|
+
const parsed = JSON.parse(raw);
|
|
14362
|
+
const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
|
|
14363
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
14364
|
+
const startedAt = typeof parsed.started_at === "string" ? parsed.started_at : "";
|
|
14365
|
+
return { pid, started_at: startedAt };
|
|
14366
|
+
} catch {
|
|
14367
|
+
return null;
|
|
14368
|
+
}
|
|
14369
|
+
}
|
|
14370
|
+
|
|
14302
14371
|
// src/direct-chat-channel.ts
|
|
14372
|
+
import { homedir } from "os";
|
|
14373
|
+
import { join as join2 } from "path";
|
|
14303
14374
|
var AGT_HOST = process.env.AGT_HOST;
|
|
14304
14375
|
var AGT_API_KEY = process.env.AGT_API_KEY;
|
|
14305
14376
|
var AGT_AGENT_ID = process.env.AGT_AGENT_ID;
|
|
14377
|
+
var DIRECT_CHAT_AGENT_DIR = AGT_AGENT_ID ? join2(homedir(), ".augmented", AGT_AGENT_ID) : null;
|
|
14306
14378
|
if (!AGT_HOST || !AGT_API_KEY || !AGT_AGENT_ID) {
|
|
14307
14379
|
process.stderr.write(
|
|
14308
14380
|
"direct-chat-channel: Missing AGT_HOST, AGT_API_KEY, or AGT_AGENT_ID. Cannot start.\n"
|
|
@@ -14488,6 +14560,22 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
14488
14560
|
throw new Error(`Unknown tool: ${name}`);
|
|
14489
14561
|
});
|
|
14490
14562
|
await mcp.connect(new StdioServerTransport());
|
|
14563
|
+
var acquiredLockPath = null;
|
|
14564
|
+
{
|
|
14565
|
+
const lockResult = acquireMcpSpawnLock({
|
|
14566
|
+
agentDir: DIRECT_CHAT_AGENT_DIR,
|
|
14567
|
+
basename: "direct-chat-channel.lock"
|
|
14568
|
+
});
|
|
14569
|
+
if (lockResult.kind === "blocked") {
|
|
14570
|
+
process.stderr.write(
|
|
14571
|
+
`direct-chat-channel: another instance pid=${lockResult.holder.pid} already running (since ${lockResult.holder.started_at}), exiting
|
|
14572
|
+
`
|
|
14573
|
+
);
|
|
14574
|
+
process.exit(0);
|
|
14575
|
+
} else if (lockResult.kind === "acquired") {
|
|
14576
|
+
acquiredLockPath = lockResult.path;
|
|
14577
|
+
}
|
|
14578
|
+
}
|
|
14491
14579
|
process.stderr.write(
|
|
14492
14580
|
`direct-chat-channel: Started (agent=${AGT_AGENT_ID}, polling=disabled \u2014 using Realtime)
|
|
14493
14581
|
`
|
|
@@ -14498,6 +14586,10 @@ function shutdown(reason) {
|
|
|
14498
14586
|
isShuttingDown = true;
|
|
14499
14587
|
process.stderr.write(`direct-chat-channel: ${reason} \u2014 exiting
|
|
14500
14588
|
`);
|
|
14589
|
+
try {
|
|
14590
|
+
releaseMcpSpawnLock(acquiredLockPath);
|
|
14591
|
+
} catch {
|
|
14592
|
+
}
|
|
14501
14593
|
setTimeout(() => process.exit(0), 100).unref();
|
|
14502
14594
|
}
|
|
14503
14595
|
process.stdin.on("close", () => shutdown("stdin closed"));
|
|
@@ -15607,7 +15607,7 @@ function createSlackBotUserIdClient(args) {
|
|
|
15607
15607
|
};
|
|
15608
15608
|
}
|
|
15609
15609
|
|
|
15610
|
-
// src/
|
|
15610
|
+
// src/mcp-spawn-lock.ts
|
|
15611
15611
|
import {
|
|
15612
15612
|
existsSync,
|
|
15613
15613
|
mkdirSync as mkdirSync2,
|
|
@@ -15617,7 +15617,6 @@ import {
|
|
|
15617
15617
|
writeFileSync as writeFileSync2
|
|
15618
15618
|
} from "fs";
|
|
15619
15619
|
import { join as join2 } from "path";
|
|
15620
|
-
var DEFAULT_LOCK_BASENAME = "slack-channel.lock";
|
|
15621
15620
|
function defaultIsPidAlive(pid) {
|
|
15622
15621
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
15623
15622
|
try {
|
|
@@ -15629,12 +15628,13 @@ function defaultIsPidAlive(pid) {
|
|
|
15629
15628
|
return true;
|
|
15630
15629
|
}
|
|
15631
15630
|
}
|
|
15632
|
-
function
|
|
15633
|
-
|
|
15634
|
-
|
|
15635
|
-
const
|
|
15636
|
-
const
|
|
15637
|
-
const
|
|
15631
|
+
function acquireMcpSpawnLock(args) {
|
|
15632
|
+
const { agentDir, basename: basename2, options = {} } = args;
|
|
15633
|
+
if (!agentDir) return { kind: "no-agent-dir" };
|
|
15634
|
+
const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
|
|
15635
|
+
const selfPid = options.selfPid ?? process.pid;
|
|
15636
|
+
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
15637
|
+
const path = join2(agentDir, basename2);
|
|
15638
15638
|
const existing = readLockHolder(path);
|
|
15639
15639
|
if (existing) {
|
|
15640
15640
|
if (existing.pid === selfPid) {
|
|
@@ -15644,14 +15644,14 @@ function acquireSlackSpawnLock(slackAgentDir, opts = {}) {
|
|
|
15644
15644
|
return { kind: "blocked", path, holder: existing };
|
|
15645
15645
|
}
|
|
15646
15646
|
}
|
|
15647
|
-
mkdirSync2(
|
|
15647
|
+
mkdirSync2(agentDir, { recursive: true, mode: 448 });
|
|
15648
15648
|
const tmpPath = `${path}.${selfPid}.tmp`;
|
|
15649
15649
|
const payload = { pid: selfPid, started_at: now() };
|
|
15650
15650
|
writeFileSync2(tmpPath, JSON.stringify(payload), { mode: 384 });
|
|
15651
15651
|
renameSync(tmpPath, path);
|
|
15652
15652
|
return { kind: "acquired", path };
|
|
15653
15653
|
}
|
|
15654
|
-
function
|
|
15654
|
+
function releaseMcpSpawnLock(lockPath, opts = {}) {
|
|
15655
15655
|
if (!lockPath) return;
|
|
15656
15656
|
const selfPid = opts.selfPid ?? process.pid;
|
|
15657
15657
|
const existing = readLockHolder(lockPath);
|
|
@@ -18133,7 +18133,7 @@ function shutdown(reason, exitCode = 0) {
|
|
|
18133
18133
|
} catch {
|
|
18134
18134
|
}
|
|
18135
18135
|
try {
|
|
18136
|
-
|
|
18136
|
+
releaseMcpSpawnLock(acquiredLockPath);
|
|
18137
18137
|
} catch {
|
|
18138
18138
|
}
|
|
18139
18139
|
setTimeout(() => process.exit(exitCode), 500).unref();
|
|
@@ -18149,7 +18149,10 @@ process.on("unhandledRejection", (reason) => {
|
|
|
18149
18149
|
`);
|
|
18150
18150
|
});
|
|
18151
18151
|
{
|
|
18152
|
-
const lockResult =
|
|
18152
|
+
const lockResult = acquireMcpSpawnLock({
|
|
18153
|
+
agentDir: SLACK_AGENT_DIR,
|
|
18154
|
+
basename: "slack-channel.lock"
|
|
18155
|
+
});
|
|
18153
18156
|
if (lockResult.kind === "blocked") {
|
|
18154
18157
|
process.stderr.write(
|
|
18155
18158
|
`slack-channel: another instance pid=${lockResult.holder.pid} already running (since ${lockResult.holder.started_at}), exiting
|
|
@@ -14296,18 +14296,18 @@ import https from "https";
|
|
|
14296
14296
|
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
14297
14297
|
import {
|
|
14298
14298
|
createWriteStream,
|
|
14299
|
-
existsSync,
|
|
14300
|
-
mkdirSync as
|
|
14301
|
-
readFileSync,
|
|
14299
|
+
existsSync as existsSync2,
|
|
14300
|
+
mkdirSync as mkdirSync3,
|
|
14301
|
+
readFileSync as readFileSync2,
|
|
14302
14302
|
readdirSync,
|
|
14303
|
-
renameSync as
|
|
14303
|
+
renameSync as renameSync3,
|
|
14304
14304
|
statSync,
|
|
14305
|
-
unlinkSync as
|
|
14305
|
+
unlinkSync as unlinkSync3,
|
|
14306
14306
|
watch,
|
|
14307
|
-
writeFileSync as
|
|
14307
|
+
writeFileSync as writeFileSync3
|
|
14308
14308
|
} from "fs";
|
|
14309
14309
|
import { homedir as homedir2 } from "os";
|
|
14310
|
-
import { join as
|
|
14310
|
+
import { join as join3 } from "path";
|
|
14311
14311
|
|
|
14312
14312
|
// src/channel-attachments.ts
|
|
14313
14313
|
import { homedir } from "os";
|
|
@@ -15741,12 +15741,82 @@ function createTelegramProgressFlush(opts) {
|
|
|
15741
15741
|
};
|
|
15742
15742
|
}
|
|
15743
15743
|
|
|
15744
|
+
// src/mcp-spawn-lock.ts
|
|
15745
|
+
import {
|
|
15746
|
+
existsSync,
|
|
15747
|
+
mkdirSync as mkdirSync2,
|
|
15748
|
+
readFileSync,
|
|
15749
|
+
renameSync as renameSync2,
|
|
15750
|
+
unlinkSync as unlinkSync2,
|
|
15751
|
+
writeFileSync as writeFileSync2
|
|
15752
|
+
} from "fs";
|
|
15753
|
+
import { join as join2 } from "path";
|
|
15754
|
+
function defaultIsPidAlive(pid) {
|
|
15755
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
15756
|
+
try {
|
|
15757
|
+
process.kill(pid, 0);
|
|
15758
|
+
return true;
|
|
15759
|
+
} catch (err) {
|
|
15760
|
+
const code = err.code;
|
|
15761
|
+
if (code === "ESRCH") return false;
|
|
15762
|
+
return true;
|
|
15763
|
+
}
|
|
15764
|
+
}
|
|
15765
|
+
function acquireMcpSpawnLock(args) {
|
|
15766
|
+
const { agentDir, basename, options = {} } = args;
|
|
15767
|
+
if (!agentDir) return { kind: "no-agent-dir" };
|
|
15768
|
+
const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
|
|
15769
|
+
const selfPid = options.selfPid ?? process.pid;
|
|
15770
|
+
const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
15771
|
+
const path = join2(agentDir, basename);
|
|
15772
|
+
const existing = readLockHolder(path);
|
|
15773
|
+
if (existing) {
|
|
15774
|
+
if (existing.pid === selfPid) {
|
|
15775
|
+
return { kind: "acquired", path };
|
|
15776
|
+
}
|
|
15777
|
+
if (isPidAlive(existing.pid)) {
|
|
15778
|
+
return { kind: "blocked", path, holder: existing };
|
|
15779
|
+
}
|
|
15780
|
+
}
|
|
15781
|
+
mkdirSync2(agentDir, { recursive: true, mode: 448 });
|
|
15782
|
+
const tmpPath = `${path}.${selfPid}.tmp`;
|
|
15783
|
+
const payload = { pid: selfPid, started_at: now() };
|
|
15784
|
+
writeFileSync2(tmpPath, JSON.stringify(payload), { mode: 384 });
|
|
15785
|
+
renameSync2(tmpPath, path);
|
|
15786
|
+
return { kind: "acquired", path };
|
|
15787
|
+
}
|
|
15788
|
+
function releaseMcpSpawnLock(lockPath, opts = {}) {
|
|
15789
|
+
if (!lockPath) return;
|
|
15790
|
+
const selfPid = opts.selfPid ?? process.pid;
|
|
15791
|
+
const existing = readLockHolder(lockPath);
|
|
15792
|
+
if (!existing) return;
|
|
15793
|
+
if (existing.pid !== selfPid) return;
|
|
15794
|
+
try {
|
|
15795
|
+
unlinkSync2(lockPath);
|
|
15796
|
+
} catch {
|
|
15797
|
+
}
|
|
15798
|
+
}
|
|
15799
|
+
function readLockHolder(path) {
|
|
15800
|
+
if (!existsSync(path)) return null;
|
|
15801
|
+
try {
|
|
15802
|
+
const raw = readFileSync(path, "utf8");
|
|
15803
|
+
const parsed = JSON.parse(raw);
|
|
15804
|
+
const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
|
|
15805
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
15806
|
+
const startedAt = typeof parsed.started_at === "string" ? parsed.started_at : "";
|
|
15807
|
+
return { pid, started_at: startedAt };
|
|
15808
|
+
} catch {
|
|
15809
|
+
return null;
|
|
15810
|
+
}
|
|
15811
|
+
}
|
|
15812
|
+
|
|
15744
15813
|
// src/telegram-channel.ts
|
|
15745
15814
|
function redactId(id) {
|
|
15746
15815
|
return createHash("sha256").update(String(id)).digest("hex").slice(0, 8);
|
|
15747
15816
|
}
|
|
15748
15817
|
var BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
|
|
15749
15818
|
var AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME ?? "unknown";
|
|
15819
|
+
var TELEGRAM_AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join3(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
|
|
15750
15820
|
var AGT_HOST = process.env.AGT_HOST ?? null;
|
|
15751
15821
|
var AGT_API_KEY = process.env.AGT_API_KEY ?? null;
|
|
15752
15822
|
var AGT_AGENT_ID = process.env.AGT_AGENT_ID ?? null;
|
|
@@ -15823,9 +15893,9 @@ if (!BOT_TOKEN) {
|
|
|
15823
15893
|
var stderrLogStream = null;
|
|
15824
15894
|
if (AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown") {
|
|
15825
15895
|
try {
|
|
15826
|
-
const logDir =
|
|
15827
|
-
|
|
15828
|
-
stderrLogStream = createWriteStream(
|
|
15896
|
+
const logDir = join3(homedir2(), ".augmented", AGENT_CODE_NAME);
|
|
15897
|
+
mkdirSync3(logDir, { recursive: true });
|
|
15898
|
+
stderrLogStream = createWriteStream(join3(logDir, "telegram-channel-stderr.log"), {
|
|
15829
15899
|
flags: "a",
|
|
15830
15900
|
mode: 384
|
|
15831
15901
|
});
|
|
@@ -15906,7 +15976,7 @@ async function setMessageReaction(chatId, messageId, emoji2) {
|
|
|
15906
15976
|
);
|
|
15907
15977
|
}
|
|
15908
15978
|
}
|
|
15909
|
-
var RESTART_FLAGS_DIR =
|
|
15979
|
+
var RESTART_FLAGS_DIR = join3(homedir2(), ".augmented", "restart-flags");
|
|
15910
15980
|
function buildTelegramHelpMessage(codeName) {
|
|
15911
15981
|
return [
|
|
15912
15982
|
`\u{1F916} *Available commands for \`${codeName}\`*`,
|
|
@@ -15943,10 +16013,10 @@ async function handleHelpCommand(opts) {
|
|
|
15943
16013
|
}
|
|
15944
16014
|
async function handleRestartCommand(opts) {
|
|
15945
16015
|
try {
|
|
15946
|
-
if (!
|
|
15947
|
-
|
|
16016
|
+
if (!existsSync2(RESTART_FLAGS_DIR)) {
|
|
16017
|
+
mkdirSync3(RESTART_FLAGS_DIR, { recursive: true });
|
|
15948
16018
|
}
|
|
15949
|
-
const flagPath =
|
|
16019
|
+
const flagPath = join3(RESTART_FLAGS_DIR, `${AGENT_CODE_NAME}.flag`);
|
|
15950
16020
|
const flag = {
|
|
15951
16021
|
codeName: AGENT_CODE_NAME,
|
|
15952
16022
|
source: "telegram",
|
|
@@ -15954,8 +16024,8 @@ async function handleRestartCommand(opts) {
|
|
|
15954
16024
|
reply: { chat_id: opts.chatId, message_id: opts.messageId }
|
|
15955
16025
|
};
|
|
15956
16026
|
const tmpPath = `${flagPath}.${process.pid}.${randomUUID2()}.tmp`;
|
|
15957
|
-
|
|
15958
|
-
|
|
16027
|
+
writeFileSync3(tmpPath, JSON.stringify(flag) + "\n", "utf8");
|
|
16028
|
+
renameSync3(tmpPath, flagPath);
|
|
15959
16029
|
process.stderr.write(
|
|
15960
16030
|
`telegram-channel(${AGENT_CODE_NAME}): /restart queued from chat ${redactId(opts.chatId)}
|
|
15961
16031
|
`
|
|
@@ -16062,16 +16132,16 @@ async function classifyRestartCommand(text) {
|
|
|
16062
16132
|
if (!ours) return "verification_failed";
|
|
16063
16133
|
return target === ours ? "act" : "ignore";
|
|
16064
16134
|
}
|
|
16065
|
-
var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ?
|
|
16066
|
-
var PENDING_INBOUND_DIR = AGENT_DIR ?
|
|
16067
|
-
var RECOVERY_OUTBOX_DIR = AGENT_DIR ?
|
|
16135
|
+
var AGENT_DIR = AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown" ? join3(homedir2(), ".augmented", AGENT_CODE_NAME) : null;
|
|
16136
|
+
var PENDING_INBOUND_DIR = AGENT_DIR ? join3(AGENT_DIR, "telegram-pending-inbound") : null;
|
|
16137
|
+
var RECOVERY_OUTBOX_DIR = AGENT_DIR ? join3(AGENT_DIR, "telegram-recovery-outbox") : null;
|
|
16068
16138
|
function safeMarkerName(chatId, messageId) {
|
|
16069
16139
|
const safe = (s) => s.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
16070
16140
|
return `${safe(chatId)}__${safe(messageId)}.json`;
|
|
16071
16141
|
}
|
|
16072
16142
|
function pendingInboundPath(chatId, messageId) {
|
|
16073
16143
|
if (!PENDING_INBOUND_DIR) return null;
|
|
16074
|
-
return
|
|
16144
|
+
return join3(PENDING_INBOUND_DIR, safeMarkerName(chatId, messageId));
|
|
16075
16145
|
}
|
|
16076
16146
|
function writePendingInboundMarker(chatId, messageId, chatType) {
|
|
16077
16147
|
const path = pendingInboundPath(chatId, messageId);
|
|
@@ -16083,8 +16153,8 @@ function writePendingInboundMarker(chatId, messageId, chatType) {
|
|
|
16083
16153
|
received_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
16084
16154
|
};
|
|
16085
16155
|
try {
|
|
16086
|
-
|
|
16087
|
-
|
|
16156
|
+
mkdirSync3(PENDING_INBOUND_DIR, { recursive: true, mode: 448 });
|
|
16157
|
+
writeFileSync3(path, JSON.stringify(marker), { mode: 384 });
|
|
16088
16158
|
} catch (err) {
|
|
16089
16159
|
process.stderr.write(
|
|
16090
16160
|
`telegram-channel(${AGENT_CODE_NAME}): pending-inbound marker write failed: ${err.message}
|
|
@@ -16096,7 +16166,7 @@ function clearPendingInboundMarker(chatId, messageId) {
|
|
|
16096
16166
|
const path = pendingInboundPath(chatId, messageId);
|
|
16097
16167
|
if (!path) return;
|
|
16098
16168
|
try {
|
|
16099
|
-
if (
|
|
16169
|
+
if (existsSync2(path)) unlinkSync3(path);
|
|
16100
16170
|
} catch {
|
|
16101
16171
|
}
|
|
16102
16172
|
}
|
|
@@ -16115,10 +16185,10 @@ function nextRetryName(filename) {
|
|
|
16115
16185
|
async function processRecoveryOutboxFile(filename) {
|
|
16116
16186
|
if (!RECOVERY_OUTBOX_DIR) return;
|
|
16117
16187
|
if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
|
|
16118
|
-
const fullPath =
|
|
16188
|
+
const fullPath = join3(RECOVERY_OUTBOX_DIR, filename);
|
|
16119
16189
|
let payload;
|
|
16120
16190
|
try {
|
|
16121
|
-
const raw =
|
|
16191
|
+
const raw = readFileSync2(fullPath, "utf-8");
|
|
16122
16192
|
payload = JSON.parse(raw);
|
|
16123
16193
|
} catch (err) {
|
|
16124
16194
|
process.stderr.write(
|
|
@@ -16126,7 +16196,7 @@ async function processRecoveryOutboxFile(filename) {
|
|
|
16126
16196
|
`
|
|
16127
16197
|
);
|
|
16128
16198
|
try {
|
|
16129
|
-
|
|
16199
|
+
renameSync3(fullPath, `${fullPath}.parse-error.poison`);
|
|
16130
16200
|
} catch {
|
|
16131
16201
|
}
|
|
16132
16202
|
return;
|
|
@@ -16137,7 +16207,7 @@ async function processRecoveryOutboxFile(filename) {
|
|
|
16137
16207
|
`
|
|
16138
16208
|
);
|
|
16139
16209
|
try {
|
|
16140
|
-
|
|
16210
|
+
renameSync3(fullPath, `${fullPath}.malformed.poison`);
|
|
16141
16211
|
} catch {
|
|
16142
16212
|
}
|
|
16143
16213
|
return;
|
|
@@ -16173,7 +16243,7 @@ async function processRecoveryOutboxFile(filename) {
|
|
|
16173
16243
|
}
|
|
16174
16244
|
if (sendSucceeded) {
|
|
16175
16245
|
try {
|
|
16176
|
-
|
|
16246
|
+
unlinkSync3(fullPath);
|
|
16177
16247
|
} catch {
|
|
16178
16248
|
}
|
|
16179
16249
|
return;
|
|
@@ -16181,7 +16251,7 @@ async function processRecoveryOutboxFile(filename) {
|
|
|
16181
16251
|
const next = nextRetryName(filename);
|
|
16182
16252
|
if (next) {
|
|
16183
16253
|
try {
|
|
16184
|
-
|
|
16254
|
+
renameSync3(fullPath, join3(RECOVERY_OUTBOX_DIR, next.next));
|
|
16185
16255
|
if (next.attempt >= MAX_RECOVERY_ATTEMPTS) {
|
|
16186
16256
|
process.stderr.write(
|
|
16187
16257
|
`telegram-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
|
|
@@ -16221,7 +16291,7 @@ function scanRecoveryRetries() {
|
|
|
16221
16291
|
if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
|
|
16222
16292
|
let mtimeMs;
|
|
16223
16293
|
try {
|
|
16224
|
-
mtimeMs = statSync(
|
|
16294
|
+
mtimeMs = statSync(join3(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
|
|
16225
16295
|
} catch {
|
|
16226
16296
|
continue;
|
|
16227
16297
|
}
|
|
@@ -16233,7 +16303,7 @@ function scanRecoveryRetries() {
|
|
|
16233
16303
|
function startRecoveryOutboxWatcher() {
|
|
16234
16304
|
if (!RECOVERY_OUTBOX_DIR) return;
|
|
16235
16305
|
try {
|
|
16236
|
-
|
|
16306
|
+
mkdirSync3(RECOVERY_OUTBOX_DIR, { recursive: true, mode: 448 });
|
|
16237
16307
|
} catch (err) {
|
|
16238
16308
|
process.stderr.write(
|
|
16239
16309
|
`telegram-channel(${AGENT_CODE_NAME}): recovery outbox mkdir failed: ${err.message}
|
|
@@ -16251,7 +16321,7 @@ function startRecoveryOutboxWatcher() {
|
|
|
16251
16321
|
const watcher = watch(RECOVERY_OUTBOX_DIR, (event, filename) => {
|
|
16252
16322
|
if (event !== "rename" || !filename) return;
|
|
16253
16323
|
if (!isFirstAttemptOutboxFile(filename)) return;
|
|
16254
|
-
if (
|
|
16324
|
+
if (existsSync2(join3(RECOVERY_OUTBOX_DIR, filename))) {
|
|
16255
16325
|
void processRecoveryOutboxFile(filename);
|
|
16256
16326
|
}
|
|
16257
16327
|
});
|
|
@@ -16272,7 +16342,7 @@ function trackPendingMessage(chatId, messageId, chatType) {
|
|
|
16272
16342
|
}
|
|
16273
16343
|
function sweepTelegramStaleMarkersOnBoot() {
|
|
16274
16344
|
if (!PENDING_INBOUND_DIR) return;
|
|
16275
|
-
if (!
|
|
16345
|
+
if (!existsSync2(PENDING_INBOUND_DIR)) return;
|
|
16276
16346
|
let filenames;
|
|
16277
16347
|
try {
|
|
16278
16348
|
filenames = readdirSync(PENDING_INBOUND_DIR);
|
|
@@ -16288,17 +16358,17 @@ function sweepTelegramStaleMarkersOnBoot() {
|
|
|
16288
16358
|
for (const filename of filenames) {
|
|
16289
16359
|
if (!filename.endsWith(".json")) continue;
|
|
16290
16360
|
if (filename.endsWith(".tmp")) continue;
|
|
16291
|
-
const fullPath =
|
|
16361
|
+
const fullPath = join3(PENDING_INBOUND_DIR, filename);
|
|
16292
16362
|
let marker;
|
|
16293
16363
|
try {
|
|
16294
|
-
marker = JSON.parse(
|
|
16364
|
+
marker = JSON.parse(readFileSync2(fullPath, "utf-8"));
|
|
16295
16365
|
} catch (err) {
|
|
16296
16366
|
process.stderr.write(
|
|
16297
16367
|
`telegram-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactId(filename)}: ${err.message}
|
|
16298
16368
|
`
|
|
16299
16369
|
);
|
|
16300
16370
|
try {
|
|
16301
|
-
|
|
16371
|
+
unlinkSync3(fullPath);
|
|
16302
16372
|
} catch {
|
|
16303
16373
|
}
|
|
16304
16374
|
cleared++;
|
|
@@ -16307,7 +16377,7 @@ function sweepTelegramStaleMarkersOnBoot() {
|
|
|
16307
16377
|
const { chat_id, message_id, received_at } = marker;
|
|
16308
16378
|
if (!chat_id || !message_id || !received_at) {
|
|
16309
16379
|
try {
|
|
16310
|
-
|
|
16380
|
+
unlinkSync3(fullPath);
|
|
16311
16381
|
} catch {
|
|
16312
16382
|
}
|
|
16313
16383
|
cleared++;
|
|
@@ -16316,7 +16386,7 @@ function sweepTelegramStaleMarkersOnBoot() {
|
|
|
16316
16386
|
const receivedAtMs = Date.parse(received_at);
|
|
16317
16387
|
if (!Number.isFinite(receivedAtMs) || receivedAtMs > now || now - receivedAtMs > STALE_MARKER_MS) {
|
|
16318
16388
|
try {
|
|
16319
|
-
|
|
16389
|
+
unlinkSync3(fullPath);
|
|
16320
16390
|
} catch {
|
|
16321
16391
|
}
|
|
16322
16392
|
cleared++;
|
|
@@ -16335,7 +16405,7 @@ function clearPendingMessage(chatId, messageId) {
|
|
|
16335
16405
|
clearPendingInboundMarker(chatId, messageId);
|
|
16336
16406
|
return;
|
|
16337
16407
|
}
|
|
16338
|
-
if (!PENDING_INBOUND_DIR || !
|
|
16408
|
+
if (!PENDING_INBOUND_DIR || !existsSync2(PENDING_INBOUND_DIR)) return;
|
|
16339
16409
|
const safeChatId = chatId.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
16340
16410
|
const prefix = `${safeChatId}__`;
|
|
16341
16411
|
let filenames;
|
|
@@ -16348,7 +16418,7 @@ function clearPendingMessage(chatId, messageId) {
|
|
|
16348
16418
|
if (!filename.startsWith(prefix)) continue;
|
|
16349
16419
|
if (!filename.endsWith(".json")) continue;
|
|
16350
16420
|
try {
|
|
16351
|
-
|
|
16421
|
+
unlinkSync3(join3(PENDING_INBOUND_DIR, filename));
|
|
16352
16422
|
} catch {
|
|
16353
16423
|
}
|
|
16354
16424
|
}
|
|
@@ -17035,6 +17105,7 @@ var telegramHttp = {
|
|
|
17035
17105
|
};
|
|
17036
17106
|
var nextOffset = 0;
|
|
17037
17107
|
var isShuttingDown = false;
|
|
17108
|
+
var acquiredLockPath = null;
|
|
17038
17109
|
async function pollLoop() {
|
|
17039
17110
|
while (!isShuttingDown) {
|
|
17040
17111
|
try {
|
|
@@ -17307,6 +17378,10 @@ function shutdown(reason) {
|
|
|
17307
17378
|
`telegram-channel(${AGENT_CODE_NAME}): ${reason} \u2014 exiting
|
|
17308
17379
|
`
|
|
17309
17380
|
);
|
|
17381
|
+
try {
|
|
17382
|
+
releaseMcpSpawnLock(acquiredLockPath);
|
|
17383
|
+
} catch {
|
|
17384
|
+
}
|
|
17310
17385
|
setTimeout(() => process.exit(0), 500).unref();
|
|
17311
17386
|
}
|
|
17312
17387
|
process.stdin.on("close", () => shutdown("stdin closed"));
|
|
@@ -17314,6 +17389,21 @@ process.stdin.on("end", () => shutdown("stdin ended"));
|
|
|
17314
17389
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
17315
17390
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
17316
17391
|
process.on("SIGHUP", () => shutdown("SIGHUP"));
|
|
17392
|
+
{
|
|
17393
|
+
const lockResult = acquireMcpSpawnLock({
|
|
17394
|
+
agentDir: TELEGRAM_AGENT_DIR,
|
|
17395
|
+
basename: "telegram-channel.lock"
|
|
17396
|
+
});
|
|
17397
|
+
if (lockResult.kind === "blocked") {
|
|
17398
|
+
process.stderr.write(
|
|
17399
|
+
`telegram-channel(${AGENT_CODE_NAME}): another instance pid=${lockResult.holder.pid} already running (since ${lockResult.holder.started_at}), exiting
|
|
17400
|
+
`
|
|
17401
|
+
);
|
|
17402
|
+
process.exit(0);
|
|
17403
|
+
} else if (lockResult.kind === "acquired") {
|
|
17404
|
+
acquiredLockPath = lockResult.path;
|
|
17405
|
+
}
|
|
17406
|
+
}
|
|
17317
17407
|
process.stderr.write(
|
|
17318
17408
|
`telegram-channel(${AGENT_CODE_NAME}): started, long-polling getUpdates
|
|
17319
17409
|
`
|