@askexenow/exe-os 0.9.13 → 0.9.14
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/backfill-conversations.js +31 -4
- package/dist/bin/backfill-responses.js +31 -4
- package/dist/bin/backfill-vectors.js +25 -4
- package/dist/bin/cleanup-stale-review-tasks.js +1 -1
- package/dist/bin/cli.js +55 -8
- package/dist/bin/exe-assign.js +31 -4
- package/dist/bin/exe-boot.js +25 -4
- package/dist/bin/exe-dispatch.js +1 -1
- package/dist/bin/exe-doctor.js +1 -1
- package/dist/bin/exe-export-behaviors.js +1 -1
- package/dist/bin/exe-forget.js +1 -1
- package/dist/bin/exe-gateway.js +447 -21
- package/dist/bin/exe-heartbeat.js +1 -1
- package/dist/bin/exe-kill.js +1 -1
- package/dist/bin/exe-launch-agent.js +1 -1
- package/dist/bin/exe-link.js +31 -4
- package/dist/bin/exe-pending-messages.js +1 -1
- package/dist/bin/exe-pending-notifications.js +1 -1
- package/dist/bin/exe-pending-reviews.js +1 -1
- package/dist/bin/exe-rename.js +31 -4
- package/dist/bin/exe-review.js +1 -1
- package/dist/bin/exe-search.js +31 -4
- package/dist/bin/exe-session-cleanup.js +25 -4
- package/dist/bin/exe-start-codex.js +1 -1
- package/dist/bin/exe-start-opencode.js +1 -1
- package/dist/bin/exe-status.js +1 -1
- package/dist/bin/exe-team.js +1 -1
- package/dist/bin/git-sweep.js +25 -4
- package/dist/bin/graph-backfill.js +1 -1
- package/dist/bin/graph-export.js +1 -1
- package/dist/bin/scan-tasks.js +25 -4
- package/dist/bin/setup.js +46 -8
- package/dist/bin/shard-migrate.js +1 -1
- package/dist/bin/wiki-sync.js +1 -1
- package/dist/gateway/index.js +128 -125
- package/dist/hooks/bug-report-worker.js +1 -1
- package/dist/hooks/codex-stop-task-finalizer.js +1 -1
- package/dist/hooks/commit-complete.js +25 -4
- package/dist/hooks/error-recall.js +31 -4
- package/dist/hooks/ingest-worker.js +25 -4
- package/dist/hooks/ingest.js +1 -1
- package/dist/hooks/instructions-loaded.js +31 -4
- package/dist/hooks/notification.js +31 -4
- package/dist/hooks/post-compact.js +31 -4
- package/dist/hooks/pre-compact.js +25 -4
- package/dist/hooks/pre-tool-use.js +31 -4
- package/dist/hooks/prompt-ingest-worker.js +25 -4
- package/dist/hooks/prompt-submit.js +25 -4
- package/dist/hooks/response-ingest-worker.js +25 -4
- package/dist/hooks/session-end.js +25 -4
- package/dist/hooks/session-start.js +31 -4
- package/dist/hooks/stop.js +25 -4
- package/dist/hooks/subagent-stop.js +31 -4
- package/dist/hooks/summary-worker.js +25 -4
- package/dist/index.js +128 -125
- package/dist/lib/cloud-sync.js +31 -4
- package/dist/lib/database.js +31 -4
- package/dist/lib/db-daemon-client.js +31 -3
- package/dist/lib/db.js +31 -4
- package/dist/lib/device-registry.js +31 -4
- package/dist/lib/embedder.js +30 -3
- package/dist/lib/exe-daemon-client.js +31 -3
- package/dist/lib/exe-daemon.js +1958 -153
- package/dist/lib/hybrid-search.js +31 -4
- package/dist/lib/schedules.js +1 -1
- package/dist/lib/store.js +1 -1
- package/dist/mcp/server.js +25 -4
- package/dist/runtime/index.js +25 -4
- package/dist/tui/App.js +34 -4
- package/package.json +1 -1
package/dist/bin/exe-gateway.js
CHANGED
|
@@ -1372,7 +1372,7 @@ async function ensureCompatibilityViews(prisma) {
|
|
|
1372
1372
|
for (const mapping of VIEW_MAPPINGS) {
|
|
1373
1373
|
const relation = mapping.source.replace(/"/g, "");
|
|
1374
1374
|
const rows = await prisma.$queryRawUnsafe(
|
|
1375
|
-
"SELECT to_regclass($1) AS regclass",
|
|
1375
|
+
"SELECT to_regclass($1)::text AS regclass",
|
|
1376
1376
|
relation
|
|
1377
1377
|
);
|
|
1378
1378
|
if (!rows[0]?.regclass) {
|
|
@@ -2811,8 +2811,29 @@ function findPackageRoot() {
|
|
|
2811
2811
|
}
|
|
2812
2812
|
return null;
|
|
2813
2813
|
}
|
|
2814
|
+
function getAvailableMemoryGB() {
|
|
2815
|
+
if (process.platform === "darwin") {
|
|
2816
|
+
try {
|
|
2817
|
+
const { execSync: execSync7 } = __require("child_process");
|
|
2818
|
+
const vmstat = execSync7("vm_stat", { encoding: "utf8" });
|
|
2819
|
+
const pageSize = 16384;
|
|
2820
|
+
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
2821
|
+
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
2822
|
+
const free = vmstat.match(/Pages free:\s+(\d+)/);
|
|
2823
|
+
const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
|
|
2824
|
+
const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
|
|
2825
|
+
const freePages = free ? parseInt(free[1], 10) : 0;
|
|
2826
|
+
const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
|
|
2827
|
+
const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
|
|
2828
|
+
return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
|
|
2829
|
+
} catch {
|
|
2830
|
+
return os5.freemem() / (1024 * 1024 * 1024);
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
return os5.freemem() / (1024 * 1024 * 1024);
|
|
2834
|
+
}
|
|
2814
2835
|
function spawnDaemon() {
|
|
2815
|
-
const freeGB =
|
|
2836
|
+
const freeGB = getAvailableMemoryGB();
|
|
2816
2837
|
const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
|
|
2817
2838
|
if (totalGB <= 8) {
|
|
2818
2839
|
process.stderr.write(
|
|
@@ -2821,9 +2842,9 @@ function spawnDaemon() {
|
|
|
2821
2842
|
);
|
|
2822
2843
|
return;
|
|
2823
2844
|
}
|
|
2824
|
-
if (totalGB <= 16 && freeGB <
|
|
2845
|
+
if (totalGB <= 16 && freeGB < 2) {
|
|
2825
2846
|
process.stderr.write(
|
|
2826
|
-
`[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB
|
|
2847
|
+
`[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
|
|
2827
2848
|
`
|
|
2828
2849
|
);
|
|
2829
2850
|
return;
|
|
@@ -6297,6 +6318,362 @@ var init_whatsapp = __esm({
|
|
|
6297
6318
|
}
|
|
6298
6319
|
});
|
|
6299
6320
|
|
|
6321
|
+
// src/gateway/adapters/signal.ts
|
|
6322
|
+
var signal_exports = {};
|
|
6323
|
+
__export(signal_exports, {
|
|
6324
|
+
SignalAdapter: () => SignalAdapter
|
|
6325
|
+
});
|
|
6326
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
6327
|
+
var DEFAULT_TIMEOUT_MS, POLL_INTERVAL_MS, SignalAdapter;
|
|
6328
|
+
var init_signal = __esm({
|
|
6329
|
+
"src/gateway/adapters/signal.ts"() {
|
|
6330
|
+
"use strict";
|
|
6331
|
+
DEFAULT_TIMEOUT_MS = 1e4;
|
|
6332
|
+
POLL_INTERVAL_MS = 2e3;
|
|
6333
|
+
SignalAdapter = class {
|
|
6334
|
+
platform = "signal";
|
|
6335
|
+
baseUrl = "";
|
|
6336
|
+
account = "";
|
|
6337
|
+
abortController = null;
|
|
6338
|
+
messageHandler = null;
|
|
6339
|
+
_connected = false;
|
|
6340
|
+
pollTimer = null;
|
|
6341
|
+
normalizeBaseUrl(url) {
|
|
6342
|
+
const trimmed = url.trim();
|
|
6343
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
6344
|
+
return trimmed.replace(/\/+$/, "");
|
|
6345
|
+
}
|
|
6346
|
+
return `http://${trimmed}`.replace(/\/+$/, "");
|
|
6347
|
+
}
|
|
6348
|
+
async connect(config2) {
|
|
6349
|
+
this.baseUrl = this.normalizeBaseUrl(
|
|
6350
|
+
config2.credentials.baseUrl ?? "localhost:8080"
|
|
6351
|
+
);
|
|
6352
|
+
this.account = config2.credentials.account ?? "";
|
|
6353
|
+
const check = await this.healthCheck();
|
|
6354
|
+
if (!check.connected) {
|
|
6355
|
+
throw new Error(
|
|
6356
|
+
`signal-cli REST API not reachable at ${this.baseUrl}. Ensure bbernhard/signal-cli-rest-api container is running.`
|
|
6357
|
+
);
|
|
6358
|
+
}
|
|
6359
|
+
this._connected = true;
|
|
6360
|
+
this.abortController = new AbortController();
|
|
6361
|
+
void this.startPolling();
|
|
6362
|
+
void this.syncContacts().catch((err) => {
|
|
6363
|
+
console.error("[signal] Contact sync failed:", err);
|
|
6364
|
+
});
|
|
6365
|
+
void this.syncGroups().catch((err) => {
|
|
6366
|
+
console.error("[signal] Group sync failed:", err);
|
|
6367
|
+
});
|
|
6368
|
+
}
|
|
6369
|
+
async disconnect() {
|
|
6370
|
+
this.abortController?.abort();
|
|
6371
|
+
this.abortController = null;
|
|
6372
|
+
if (this.pollTimer) {
|
|
6373
|
+
clearTimeout(this.pollTimer);
|
|
6374
|
+
this.pollTimer = null;
|
|
6375
|
+
}
|
|
6376
|
+
this._connected = false;
|
|
6377
|
+
}
|
|
6378
|
+
onMessage(handler) {
|
|
6379
|
+
this.messageHandler = handler;
|
|
6380
|
+
}
|
|
6381
|
+
async sendText(channelId, text, _options) {
|
|
6382
|
+
const isGroup = channelId.startsWith("group:");
|
|
6383
|
+
const body = {
|
|
6384
|
+
message: text,
|
|
6385
|
+
number: this.account,
|
|
6386
|
+
...isGroup ? { recipients: [], group_id: channelId.slice("group:".length) } : { recipients: [channelId] }
|
|
6387
|
+
};
|
|
6388
|
+
const res = await fetch(`${this.baseUrl}/v2/send`, {
|
|
6389
|
+
method: "POST",
|
|
6390
|
+
headers: { "Content-Type": "application/json" },
|
|
6391
|
+
body: JSON.stringify(body),
|
|
6392
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
|
|
6393
|
+
});
|
|
6394
|
+
if (!res.ok) {
|
|
6395
|
+
const errText = await res.text().catch(() => "");
|
|
6396
|
+
throw new Error(`Signal send failed (${res.status}): ${errText}`);
|
|
6397
|
+
}
|
|
6398
|
+
}
|
|
6399
|
+
async sendTyping(channelId) {
|
|
6400
|
+
try {
|
|
6401
|
+
const isGroup = channelId.startsWith("group:");
|
|
6402
|
+
const endpoint = isGroup ? `${this.baseUrl}/v1/typing-indicator/${this.account}` : `${this.baseUrl}/v1/typing-indicator/${this.account}`;
|
|
6403
|
+
await fetch(endpoint, {
|
|
6404
|
+
method: "PUT",
|
|
6405
|
+
headers: { "Content-Type": "application/json" },
|
|
6406
|
+
body: JSON.stringify({
|
|
6407
|
+
recipient: isGroup ? void 0 : channelId,
|
|
6408
|
+
group_id: isGroup ? channelId.slice("group:".length) : void 0
|
|
6409
|
+
}),
|
|
6410
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
|
|
6411
|
+
});
|
|
6412
|
+
} catch {
|
|
6413
|
+
}
|
|
6414
|
+
}
|
|
6415
|
+
async healthCheck() {
|
|
6416
|
+
if (!this.baseUrl) return { connected: this._connected };
|
|
6417
|
+
const start = Date.now();
|
|
6418
|
+
try {
|
|
6419
|
+
const res = await fetch(`${this.baseUrl}/v1/about`, {
|
|
6420
|
+
method: "GET",
|
|
6421
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
|
|
6422
|
+
});
|
|
6423
|
+
this._connected = res.ok;
|
|
6424
|
+
return {
|
|
6425
|
+
connected: this._connected,
|
|
6426
|
+
latencyMs: Date.now() - start
|
|
6427
|
+
};
|
|
6428
|
+
} catch {
|
|
6429
|
+
this._connected = false;
|
|
6430
|
+
return { connected: false };
|
|
6431
|
+
}
|
|
6432
|
+
}
|
|
6433
|
+
async startPolling() {
|
|
6434
|
+
while (this.abortController && !this.abortController.signal.aborted) {
|
|
6435
|
+
try {
|
|
6436
|
+
await this.pollMessages();
|
|
6437
|
+
} catch (err) {
|
|
6438
|
+
if (this.abortController?.signal.aborted) return;
|
|
6439
|
+
console.error("[signal] Poll error:", err);
|
|
6440
|
+
}
|
|
6441
|
+
await new Promise((resolve) => {
|
|
6442
|
+
this.pollTimer = setTimeout(resolve, POLL_INTERVAL_MS);
|
|
6443
|
+
});
|
|
6444
|
+
}
|
|
6445
|
+
}
|
|
6446
|
+
async pollMessages() {
|
|
6447
|
+
if (!this.account || !this.messageHandler) return;
|
|
6448
|
+
const res = await fetch(
|
|
6449
|
+
`${this.baseUrl}/v1/receive/${encodeURIComponent(this.account)}`,
|
|
6450
|
+
{
|
|
6451
|
+
method: "GET",
|
|
6452
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
|
|
6453
|
+
}
|
|
6454
|
+
);
|
|
6455
|
+
if (!res.ok) {
|
|
6456
|
+
if (res.status === 400) {
|
|
6457
|
+
return;
|
|
6458
|
+
}
|
|
6459
|
+
throw new Error(`Signal receive failed (${res.status})`);
|
|
6460
|
+
}
|
|
6461
|
+
const entries = await res.json();
|
|
6462
|
+
if (!Array.isArray(entries) || entries.length === 0) return;
|
|
6463
|
+
for (const entry of entries) {
|
|
6464
|
+
await this.handleEntry(entry);
|
|
6465
|
+
}
|
|
6466
|
+
}
|
|
6467
|
+
async handleEntry(entry) {
|
|
6468
|
+
if (!this.messageHandler) return;
|
|
6469
|
+
const envelope = entry.envelope;
|
|
6470
|
+
if (!envelope) return;
|
|
6471
|
+
const senderId = envelope.sourceNumber ?? envelope.source ?? envelope.sourceUuid ?? "";
|
|
6472
|
+
if (envelope.dataMessage?.reaction) {
|
|
6473
|
+
const rm = envelope.dataMessage.reaction;
|
|
6474
|
+
if (rm.isRemove) return;
|
|
6475
|
+
const normalized2 = {
|
|
6476
|
+
messageId: randomUUID5(),
|
|
6477
|
+
platform: "signal",
|
|
6478
|
+
senderId,
|
|
6479
|
+
senderName: envelope.sourceName ?? void 0,
|
|
6480
|
+
channelId: senderId,
|
|
6481
|
+
chatType: "direct",
|
|
6482
|
+
text: "",
|
|
6483
|
+
timestamp: new Date(
|
|
6484
|
+
envelope.timestamp ?? Date.now()
|
|
6485
|
+
).toISOString(),
|
|
6486
|
+
raw: entry,
|
|
6487
|
+
dataCategory: "reaction",
|
|
6488
|
+
reaction: {
|
|
6489
|
+
emoji: rm.emoji ?? "",
|
|
6490
|
+
targetMessageId: String(rm.targetSentTimestamp ?? ""),
|
|
6491
|
+
reactedBy: senderId,
|
|
6492
|
+
timestamp: new Date(
|
|
6493
|
+
envelope.timestamp ?? Date.now()
|
|
6494
|
+
).toISOString()
|
|
6495
|
+
}
|
|
6496
|
+
};
|
|
6497
|
+
await this.emitMessage(normalized2);
|
|
6498
|
+
return;
|
|
6499
|
+
}
|
|
6500
|
+
if (envelope.receiptMessage) {
|
|
6501
|
+
const rcpt = envelope.receiptMessage;
|
|
6502
|
+
for (const ts2 of rcpt.timestamps ?? []) {
|
|
6503
|
+
const normalized2 = {
|
|
6504
|
+
messageId: randomUUID5(),
|
|
6505
|
+
platform: "signal",
|
|
6506
|
+
senderId,
|
|
6507
|
+
senderName: envelope.sourceName ?? void 0,
|
|
6508
|
+
channelId: senderId,
|
|
6509
|
+
chatType: "direct",
|
|
6510
|
+
text: "",
|
|
6511
|
+
timestamp: new Date(
|
|
6512
|
+
envelope.timestamp ?? Date.now()
|
|
6513
|
+
).toISOString(),
|
|
6514
|
+
raw: entry,
|
|
6515
|
+
dataCategory: "read_receipt",
|
|
6516
|
+
readReceipt: {
|
|
6517
|
+
messageId: String(ts2),
|
|
6518
|
+
status: rcpt.type === "READ" ? "read" : "delivered",
|
|
6519
|
+
timestamp: new Date(
|
|
6520
|
+
envelope.timestamp ?? Date.now()
|
|
6521
|
+
).toISOString(),
|
|
6522
|
+
readBy: senderId
|
|
6523
|
+
}
|
|
6524
|
+
};
|
|
6525
|
+
await this.emitMessage(normalized2);
|
|
6526
|
+
}
|
|
6527
|
+
return;
|
|
6528
|
+
}
|
|
6529
|
+
if (envelope.editMessage) {
|
|
6530
|
+
const em = envelope.editMessage;
|
|
6531
|
+
const dm2 = em.dataMessage;
|
|
6532
|
+
if (!dm2) return;
|
|
6533
|
+
const isGroup2 = !!dm2.groupInfo?.groupId;
|
|
6534
|
+
const normalized2 = {
|
|
6535
|
+
messageId: String(dm2.timestamp ?? randomUUID5()),
|
|
6536
|
+
platform: "signal",
|
|
6537
|
+
senderId,
|
|
6538
|
+
senderName: envelope.sourceName ?? void 0,
|
|
6539
|
+
channelId: isGroup2 ? `group:${dm2.groupInfo.groupId ?? ""}` : senderId,
|
|
6540
|
+
chatType: isGroup2 ? "group" : "direct",
|
|
6541
|
+
text: dm2.message ?? "",
|
|
6542
|
+
timestamp: new Date(
|
|
6543
|
+
envelope.timestamp ?? Date.now()
|
|
6544
|
+
).toISOString(),
|
|
6545
|
+
media: this.extractMedia(dm2.attachments),
|
|
6546
|
+
raw: entry,
|
|
6547
|
+
dataCategory: "edit",
|
|
6548
|
+
replyTo: {
|
|
6549
|
+
messageId: String(em.targetSentTimestamp ?? ""),
|
|
6550
|
+
text: "",
|
|
6551
|
+
senderId
|
|
6552
|
+
}
|
|
6553
|
+
};
|
|
6554
|
+
await this.emitMessage(normalized2);
|
|
6555
|
+
return;
|
|
6556
|
+
}
|
|
6557
|
+
if (!envelope.dataMessage?.message) return;
|
|
6558
|
+
const dm = envelope.dataMessage;
|
|
6559
|
+
const isGroup = !!dm.groupInfo?.groupId;
|
|
6560
|
+
const normalized = {
|
|
6561
|
+
messageId: String(dm.timestamp ?? randomUUID5()),
|
|
6562
|
+
platform: "signal",
|
|
6563
|
+
senderId,
|
|
6564
|
+
senderName: envelope.sourceName ?? void 0,
|
|
6565
|
+
channelId: isGroup ? `group:${dm.groupInfo.groupId ?? ""}` : senderId,
|
|
6566
|
+
chatType: isGroup ? "group" : "direct",
|
|
6567
|
+
text: dm.message,
|
|
6568
|
+
timestamp: new Date(
|
|
6569
|
+
envelope.timestamp ?? Date.now()
|
|
6570
|
+
).toISOString(),
|
|
6571
|
+
media: this.extractMedia(dm.attachments),
|
|
6572
|
+
replyTo: dm.quote ? {
|
|
6573
|
+
messageId: String(dm.quote.id ?? ""),
|
|
6574
|
+
text: dm.quote.text ?? "",
|
|
6575
|
+
senderId: dm.quote.author ?? ""
|
|
6576
|
+
} : void 0,
|
|
6577
|
+
raw: entry,
|
|
6578
|
+
dataCategory: "message"
|
|
6579
|
+
};
|
|
6580
|
+
await this.emitMessage(normalized);
|
|
6581
|
+
}
|
|
6582
|
+
async emitMessage(msg) {
|
|
6583
|
+
if (!this.messageHandler) return;
|
|
6584
|
+
try {
|
|
6585
|
+
await this.messageHandler(msg);
|
|
6586
|
+
} catch (err) {
|
|
6587
|
+
console.error("[signal] Message handler error:", err);
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6590
|
+
/** Import all Signal contacts */
|
|
6591
|
+
async syncContacts() {
|
|
6592
|
+
if (!this.messageHandler || !this.account) return;
|
|
6593
|
+
const res = await fetch(
|
|
6594
|
+
`${this.baseUrl}/v1/contacts/${encodeURIComponent(this.account)}`,
|
|
6595
|
+
{ signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS) }
|
|
6596
|
+
);
|
|
6597
|
+
if (!res.ok) return;
|
|
6598
|
+
const contacts = await res.json();
|
|
6599
|
+
if (!Array.isArray(contacts)) return;
|
|
6600
|
+
for (const contact of contacts) {
|
|
6601
|
+
const phone = contact.number ?? "";
|
|
6602
|
+
if (!phone) continue;
|
|
6603
|
+
const name = contact.name ?? contact.profileName ?? phone;
|
|
6604
|
+
const normalized = {
|
|
6605
|
+
messageId: randomUUID5(),
|
|
6606
|
+
platform: "signal",
|
|
6607
|
+
senderId: phone,
|
|
6608
|
+
senderName: name,
|
|
6609
|
+
channelId: phone,
|
|
6610
|
+
chatType: "direct",
|
|
6611
|
+
text: "",
|
|
6612
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6613
|
+
raw: contact,
|
|
6614
|
+
dataCategory: "contact_sync",
|
|
6615
|
+
contactSync: {
|
|
6616
|
+
name,
|
|
6617
|
+
phone,
|
|
6618
|
+
pushName: contact.profileName ?? void 0
|
|
6619
|
+
}
|
|
6620
|
+
};
|
|
6621
|
+
await this.emitMessage(normalized);
|
|
6622
|
+
}
|
|
6623
|
+
console.log(`[signal] Synced ${contacts.length} contacts`);
|
|
6624
|
+
}
|
|
6625
|
+
/** Import all Signal groups */
|
|
6626
|
+
async syncGroups() {
|
|
6627
|
+
if (!this.messageHandler || !this.account) return;
|
|
6628
|
+
const res = await fetch(
|
|
6629
|
+
`${this.baseUrl}/v1/groups/${encodeURIComponent(this.account)}`,
|
|
6630
|
+
{ signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS) }
|
|
6631
|
+
);
|
|
6632
|
+
if (!res.ok) return;
|
|
6633
|
+
const groups = await res.json();
|
|
6634
|
+
if (!Array.isArray(groups)) return;
|
|
6635
|
+
for (const group of groups) {
|
|
6636
|
+
const normalized = {
|
|
6637
|
+
messageId: randomUUID5(),
|
|
6638
|
+
platform: "signal",
|
|
6639
|
+
senderId: `group:${group.id}`,
|
|
6640
|
+
channelId: `group:${group.id}`,
|
|
6641
|
+
chatType: "group",
|
|
6642
|
+
text: "",
|
|
6643
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6644
|
+
raw: group,
|
|
6645
|
+
dataCategory: "group",
|
|
6646
|
+
groupInfo: {
|
|
6647
|
+
groupId: group.id,
|
|
6648
|
+
groupName: group.name ?? "",
|
|
6649
|
+
participants: group.members ?? [],
|
|
6650
|
+
admins: group.admins ?? [],
|
|
6651
|
+
description: group.description ?? void 0
|
|
6652
|
+
}
|
|
6653
|
+
};
|
|
6654
|
+
await this.emitMessage(normalized);
|
|
6655
|
+
}
|
|
6656
|
+
console.log(`[signal] Synced ${groups.length} groups`);
|
|
6657
|
+
}
|
|
6658
|
+
extractMedia(attachments) {
|
|
6659
|
+
if (!attachments?.length) return void 0;
|
|
6660
|
+
return attachments.map((att) => {
|
|
6661
|
+
const ct = att.contentType ?? "";
|
|
6662
|
+
let type = "document";
|
|
6663
|
+
if (ct.startsWith("image/")) type = "image";
|
|
6664
|
+
else if (ct.startsWith("video/")) type = "video";
|
|
6665
|
+
else if (ct.startsWith("audio/")) type = "audio";
|
|
6666
|
+
return {
|
|
6667
|
+
type,
|
|
6668
|
+
fileName: att.filename ?? void 0,
|
|
6669
|
+
url: att.id ? `${this.baseUrl}/v1/attachments/${att.id}` : void 0
|
|
6670
|
+
};
|
|
6671
|
+
});
|
|
6672
|
+
}
|
|
6673
|
+
};
|
|
6674
|
+
}
|
|
6675
|
+
});
|
|
6676
|
+
|
|
6300
6677
|
// src/gateway/adapters/telegram.ts
|
|
6301
6678
|
var telegram_exports = {};
|
|
6302
6679
|
__export(telegram_exports, {
|
|
@@ -6599,7 +6976,7 @@ var slack_exports = {};
|
|
|
6599
6976
|
__export(slack_exports, {
|
|
6600
6977
|
SlackAdapter: () => SlackAdapter
|
|
6601
6978
|
});
|
|
6602
|
-
import { randomUUID as
|
|
6979
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
6603
6980
|
var SlackAdapter;
|
|
6604
6981
|
var init_slack = __esm({
|
|
6605
6982
|
"src/gateway/adapters/slack.ts"() {
|
|
@@ -6641,7 +7018,7 @@ var init_slack = __esm({
|
|
|
6641
7018
|
if (event.subtype) return;
|
|
6642
7019
|
const isGroup = event.channel_type !== "im";
|
|
6643
7020
|
const normalized = {
|
|
6644
|
-
messageId: event.client_msg_id ?? event.ts ??
|
|
7021
|
+
messageId: event.client_msg_id ?? event.ts ?? randomUUID6(),
|
|
6645
7022
|
platform: "slack",
|
|
6646
7023
|
senderId: event.user ?? "",
|
|
6647
7024
|
channelId: event.channel ?? "",
|
|
@@ -6703,7 +7080,7 @@ var init_slack = __esm({
|
|
|
6703
7080
|
if (!event.text) return;
|
|
6704
7081
|
const isGroup = event.channel_type !== "im";
|
|
6705
7082
|
const normalized = {
|
|
6706
|
-
messageId: event.ts ??
|
|
7083
|
+
messageId: event.ts ?? randomUUID6(),
|
|
6707
7084
|
platform: "slack",
|
|
6708
7085
|
senderId: event.user ?? "",
|
|
6709
7086
|
senderName: event.user_profile?.display_name ?? event.user_profile?.real_name ?? void 0,
|
|
@@ -6750,12 +7127,12 @@ import { execFile } from "child_process";
|
|
|
6750
7127
|
import { promisify } from "util";
|
|
6751
7128
|
import os8 from "os";
|
|
6752
7129
|
import path11 from "path";
|
|
6753
|
-
var execFileAsync,
|
|
7130
|
+
var execFileAsync, POLL_INTERVAL_MS2, MESSAGES_DB_PATH, IMessageAdapter;
|
|
6754
7131
|
var init_imessage = __esm({
|
|
6755
7132
|
"src/gateway/adapters/imessage.ts"() {
|
|
6756
7133
|
"use strict";
|
|
6757
7134
|
execFileAsync = promisify(execFile);
|
|
6758
|
-
|
|
7135
|
+
POLL_INTERVAL_MS2 = 5e3;
|
|
6759
7136
|
MESSAGES_DB_PATH = path11.join(
|
|
6760
7137
|
process.env.HOME ?? os8.homedir(),
|
|
6761
7138
|
"Library/Messages/chat.db"
|
|
@@ -6792,7 +7169,7 @@ var init_imessage = __esm({
|
|
|
6792
7169
|
console.log("[imessage] Connected via AppleScript bridge");
|
|
6793
7170
|
this.pollTimer = setInterval(() => {
|
|
6794
7171
|
void this.pollMessages();
|
|
6795
|
-
},
|
|
7172
|
+
}, POLL_INTERVAL_MS2);
|
|
6796
7173
|
}
|
|
6797
7174
|
async disconnect() {
|
|
6798
7175
|
if (this.pollTimer) {
|
|
@@ -6926,7 +7303,7 @@ var email_exports = {};
|
|
|
6926
7303
|
__export(email_exports, {
|
|
6927
7304
|
EmailAdapter: () => EmailAdapter
|
|
6928
7305
|
});
|
|
6929
|
-
import { randomUUID as
|
|
7306
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
6930
7307
|
import { createTransport } from "nodemailer";
|
|
6931
7308
|
function extractEmailAddress(from) {
|
|
6932
7309
|
const match = from.match(/<([^>]+)>/);
|
|
@@ -7008,7 +7385,7 @@ var init_email = __esm({
|
|
|
7008
7385
|
const senderEmail = extractEmailAddress(from);
|
|
7009
7386
|
const media = this.extractMedia(payload);
|
|
7010
7387
|
const normalized = {
|
|
7011
|
-
messageId: payload.message_id ??
|
|
7388
|
+
messageId: payload.message_id ?? randomUUID7(),
|
|
7012
7389
|
platform: "email",
|
|
7013
7390
|
senderId: senderEmail,
|
|
7014
7391
|
senderName: extractSenderName(from),
|
|
@@ -7069,7 +7446,7 @@ var webhook_exports = {};
|
|
|
7069
7446
|
__export(webhook_exports, {
|
|
7070
7447
|
WebhookAdapter: () => WebhookAdapter
|
|
7071
7448
|
});
|
|
7072
|
-
import { randomUUID as
|
|
7449
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
7073
7450
|
function resolvePath(obj, path24) {
|
|
7074
7451
|
let current = obj;
|
|
7075
7452
|
for (const segment of path24.split(".")) {
|
|
@@ -7125,7 +7502,7 @@ var init_webhook = __esm({
|
|
|
7125
7502
|
if (!text || !senderId) return;
|
|
7126
7503
|
const channelId = this.fieldMap.channelId ? String(resolvePath(rawPayload, this.fieldMap.channelId) ?? senderId) : senderId;
|
|
7127
7504
|
const senderName = this.fieldMap.senderName ? String(resolvePath(rawPayload, this.fieldMap.senderName) ?? "") || void 0 : void 0;
|
|
7128
|
-
const messageId = this.fieldMap.messageId ? String(resolvePath(rawPayload, this.fieldMap.messageId) ??
|
|
7505
|
+
const messageId = this.fieldMap.messageId ? String(resolvePath(rawPayload, this.fieldMap.messageId) ?? randomUUID8()) : randomUUID8();
|
|
7129
7506
|
const timestamp = this.fieldMap.timestamp ? String(resolvePath(rawPayload, this.fieldMap.timestamp) ?? (/* @__PURE__ */ new Date()).toISOString()) : (/* @__PURE__ */ new Date()).toISOString();
|
|
7130
7507
|
const normalized = {
|
|
7131
7508
|
messageId,
|
|
@@ -10888,7 +11265,7 @@ var init_messaging = __esm({
|
|
|
10888
11265
|
|
|
10889
11266
|
// src/automation/trigger-engine.ts
|
|
10890
11267
|
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
|
|
10891
|
-
import { randomUUID as
|
|
11268
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
10892
11269
|
import path22 from "path";
|
|
10893
11270
|
import os14 from "os";
|
|
10894
11271
|
function substituteTemplate(template, record) {
|
|
@@ -11251,6 +11628,7 @@ var init_crm_webhook = __esm({
|
|
|
11251
11628
|
import { existsSync as existsSync18, readFileSync as readFileSync15 } from "fs";
|
|
11252
11629
|
import path23 from "path";
|
|
11253
11630
|
import os15 from "os";
|
|
11631
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11254
11632
|
|
|
11255
11633
|
// src/gateway/webhook-server.ts
|
|
11256
11634
|
import {
|
|
@@ -12448,6 +12826,25 @@ init_employees();
|
|
|
12448
12826
|
var CONFIG_DIR = process.env.EXE_GATEWAY_HOME || path23.join(os15.homedir(), ".exe-os");
|
|
12449
12827
|
var CONFIG_PATH3 = process.env.EXE_GATEWAY_CONFIG || path23.join(CONFIG_DIR, "gateway.json");
|
|
12450
12828
|
var DEFAULT_PORT = 3100;
|
|
12829
|
+
function validateApiRouterProductionEnv(env = process.env, logger = console) {
|
|
12830
|
+
if (env.NODE_ENV !== "production") return;
|
|
12831
|
+
const apiRouterUrl = env.API_ROUTER_URL;
|
|
12832
|
+
const apiRouterKey = env.API_ROUTER_KEY;
|
|
12833
|
+
const byokEnabled = env.BYOK_ENABLED === "true";
|
|
12834
|
+
const rawAnthropicKey = env.ANTHROPIC_API_KEY;
|
|
12835
|
+
if (byokEnabled) return;
|
|
12836
|
+
if (!apiRouterUrl || !apiRouterKey) {
|
|
12837
|
+
logger.error(
|
|
12838
|
+
"[exe-gateway] FATAL: API_ROUTER_URL and API_ROUTER_KEY are required.\n Customer deploys must route AI requests through the Exe API Router.\n Set API_ROUTER_URL=https://gateway.askexe.com and API_ROUTER_KEY=<your-key>.\n To use your own API keys instead, set BYOK_ENABLED=true."
|
|
12839
|
+
);
|
|
12840
|
+
throw new Error("API_ROUTER_REQUIRED");
|
|
12841
|
+
}
|
|
12842
|
+
if (rawAnthropicKey) {
|
|
12843
|
+
logger.warn(
|
|
12844
|
+
"[exe-gateway] WARNING: ANTHROPIC_API_KEY is set but BYOK_ENABLED is false.\n AI requests will route through the API Router (API_ROUTER_URL), not the raw key.\n To use your own key, set BYOK_ENABLED=true."
|
|
12845
|
+
);
|
|
12846
|
+
}
|
|
12847
|
+
}
|
|
12451
12848
|
function loadConfig2() {
|
|
12452
12849
|
if (!existsSync18(CONFIG_PATH3)) {
|
|
12453
12850
|
console.log(
|
|
@@ -12477,6 +12874,11 @@ async function main() {
|
|
|
12477
12874
|
console.error(`[exe-gateway] ${err instanceof Error ? err.message : String(err)}`);
|
|
12478
12875
|
process.exit(1);
|
|
12479
12876
|
}
|
|
12877
|
+
try {
|
|
12878
|
+
validateApiRouterProductionEnv(process.env);
|
|
12879
|
+
} catch {
|
|
12880
|
+
process.exit(1);
|
|
12881
|
+
}
|
|
12480
12882
|
const config2 = loadConfig2();
|
|
12481
12883
|
const port = config2.port ?? DEFAULT_PORT;
|
|
12482
12884
|
const server = new WebhookServer({
|
|
@@ -12510,6 +12912,21 @@ async function main() {
|
|
|
12510
12912
|
});
|
|
12511
12913
|
console.log("[exe-gateway] WhatsApp adapter registered");
|
|
12512
12914
|
}
|
|
12915
|
+
if (adapters.signal?.enabled) {
|
|
12916
|
+
const { SignalAdapter: SignalAdapter2 } = await Promise.resolve().then(() => (init_signal(), signal_exports));
|
|
12917
|
+
const signal = new SignalAdapter2();
|
|
12918
|
+
gateway.registerAdapter(signal);
|
|
12919
|
+
const signalAccount = adapters.signal.accounts?.[0];
|
|
12920
|
+
platformConfigs.set("signal", {
|
|
12921
|
+
platform: "signal",
|
|
12922
|
+
permissions: { canRead: true, canWrite: true, canExecute: true },
|
|
12923
|
+
credentials: {
|
|
12924
|
+
baseUrl: signalAccount?.signalCliUrl ?? "http://exe-signal-cli:8080",
|
|
12925
|
+
account: signalAccount?.account ?? ""
|
|
12926
|
+
}
|
|
12927
|
+
});
|
|
12928
|
+
console.log("[exe-gateway] Signal adapter registered");
|
|
12929
|
+
}
|
|
12513
12930
|
if (adapters.telegram?.enabled) {
|
|
12514
12931
|
const { TelegramAdapter: TelegramAdapter2 } = await Promise.resolve().then(() => (init_telegram(), telegram_exports));
|
|
12515
12932
|
const telegram = new TelegramAdapter2();
|
|
@@ -12600,9 +13017,18 @@ async function main() {
|
|
|
12600
13017
|
await server.start();
|
|
12601
13018
|
console.log(`[exe-gateway] Ready on port ${port}`);
|
|
12602
13019
|
}
|
|
12603
|
-
|
|
12604
|
-
const
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
13020
|
+
function isDirectExecution() {
|
|
13021
|
+
const entrypoint = process.argv[1];
|
|
13022
|
+
return Boolean(entrypoint && path23.resolve(entrypoint) === fileURLToPath3(import.meta.url));
|
|
13023
|
+
}
|
|
13024
|
+
if (isDirectExecution()) {
|
|
13025
|
+
main().catch((err) => {
|
|
13026
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
13027
|
+
console.error(`[exe-gateway] Fatal: ${msg}`);
|
|
13028
|
+
if (process.env.DEBUG) console.error(err);
|
|
13029
|
+
process.exit(1);
|
|
13030
|
+
});
|
|
13031
|
+
}
|
|
13032
|
+
export {
|
|
13033
|
+
validateApiRouterProductionEnv
|
|
13034
|
+
};
|
|
@@ -674,7 +674,7 @@ async function ensureCompatibilityViews(prisma) {
|
|
|
674
674
|
for (const mapping of VIEW_MAPPINGS) {
|
|
675
675
|
const relation = mapping.source.replace(/"/g, "");
|
|
676
676
|
const rows = await prisma.$queryRawUnsafe(
|
|
677
|
-
"SELECT to_regclass($1) AS regclass",
|
|
677
|
+
"SELECT to_regclass($1)::text AS regclass",
|
|
678
678
|
relation
|
|
679
679
|
);
|
|
680
680
|
if (!rows[0]?.regclass) {
|
package/dist/bin/exe-kill.js
CHANGED
|
@@ -644,7 +644,7 @@ async function ensureCompatibilityViews(prisma) {
|
|
|
644
644
|
for (const mapping of VIEW_MAPPINGS) {
|
|
645
645
|
const relation = mapping.source.replace(/"/g, "");
|
|
646
646
|
const rows = await prisma.$queryRawUnsafe(
|
|
647
|
-
"SELECT to_regclass($1) AS regclass",
|
|
647
|
+
"SELECT to_regclass($1)::text AS regclass",
|
|
648
648
|
relation
|
|
649
649
|
);
|
|
650
650
|
if (!rows[0]?.regclass) {
|
|
@@ -995,7 +995,7 @@ async function ensureCompatibilityViews(prisma) {
|
|
|
995
995
|
for (const mapping of VIEW_MAPPINGS) {
|
|
996
996
|
const relation = mapping.source.replace(/"/g, "");
|
|
997
997
|
const rows = await prisma.$queryRawUnsafe(
|
|
998
|
-
"SELECT to_regclass($1) AS regclass",
|
|
998
|
+
"SELECT to_regclass($1)::text AS regclass",
|
|
999
999
|
relation
|
|
1000
1000
|
);
|
|
1001
1001
|
if (!rows[0]?.regclass) {
|
package/dist/bin/exe-link.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
4
10
|
var __esm = (fn, res) => function __init() {
|
|
5
11
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
12
|
};
|
|
@@ -949,7 +955,7 @@ async function ensureCompatibilityViews(prisma) {
|
|
|
949
955
|
for (const mapping of VIEW_MAPPINGS) {
|
|
950
956
|
const relation = mapping.source.replace(/"/g, "");
|
|
951
957
|
const rows = await prisma.$queryRawUnsafe(
|
|
952
|
-
"SELECT to_regclass($1) AS regclass",
|
|
958
|
+
"SELECT to_regclass($1)::text AS regclass",
|
|
953
959
|
relation
|
|
954
960
|
);
|
|
955
961
|
if (!rows[0]?.regclass) {
|
|
@@ -1303,8 +1309,29 @@ function findPackageRoot() {
|
|
|
1303
1309
|
}
|
|
1304
1310
|
return null;
|
|
1305
1311
|
}
|
|
1312
|
+
function getAvailableMemoryGB() {
|
|
1313
|
+
if (process.platform === "darwin") {
|
|
1314
|
+
try {
|
|
1315
|
+
const { execSync: execSync2 } = __require("child_process");
|
|
1316
|
+
const vmstat = execSync2("vm_stat", { encoding: "utf8" });
|
|
1317
|
+
const pageSize = 16384;
|
|
1318
|
+
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
1319
|
+
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
1320
|
+
const free = vmstat.match(/Pages free:\s+(\d+)/);
|
|
1321
|
+
const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
|
|
1322
|
+
const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
|
|
1323
|
+
const freePages = free ? parseInt(free[1], 10) : 0;
|
|
1324
|
+
const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
|
|
1325
|
+
const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
|
|
1326
|
+
return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
|
|
1327
|
+
} catch {
|
|
1328
|
+
return os5.freemem() / (1024 * 1024 * 1024);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
return os5.freemem() / (1024 * 1024 * 1024);
|
|
1332
|
+
}
|
|
1306
1333
|
function spawnDaemon() {
|
|
1307
|
-
const freeGB =
|
|
1334
|
+
const freeGB = getAvailableMemoryGB();
|
|
1308
1335
|
const totalGB = os5.totalmem() / (1024 * 1024 * 1024);
|
|
1309
1336
|
if (totalGB <= 8) {
|
|
1310
1337
|
process.stderr.write(
|
|
@@ -1313,9 +1340,9 @@ function spawnDaemon() {
|
|
|
1313
1340
|
);
|
|
1314
1341
|
return;
|
|
1315
1342
|
}
|
|
1316
|
-
if (totalGB <= 16 && freeGB <
|
|
1343
|
+
if (totalGB <= 16 && freeGB < 2) {
|
|
1317
1344
|
process.stderr.write(
|
|
1318
|
-
`[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB
|
|
1345
|
+
`[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
|
|
1319
1346
|
`
|
|
1320
1347
|
);
|
|
1321
1348
|
return;
|