agent-relay 1.3.0 → 1.3.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/.trajectories/active/traj_3yx9dy148mge.json +42 -0
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.json +49 -0
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.md +31 -0
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.json +49 -0
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.md +31 -0
- package/.trajectories/completed/2026-01/traj_6unwwmgyj5sq.json +109 -0
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.json +49 -0
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.md +31 -0
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.json +66 -0
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.md +36 -0
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.json +49 -0
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.md +31 -0
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.json +65 -0
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.md +37 -0
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.json +36 -0
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.md +21 -0
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.json +101 -0
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.md +52 -0
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.json +61 -0
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.md +36 -0
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.json +73 -0
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.md +41 -0
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.json +77 -0
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.md +42 -0
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.json +109 -0
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.md +56 -0
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.json +113 -0
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.md +57 -0
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.json +61 -0
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.md +36 -0
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.json +49 -0
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.md +31 -0
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.json +49 -0
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.md +31 -0
- package/.trajectories/index.json +140 -1
- package/TRAIL_GIT_AUTH_FIX.md +113 -0
- package/deploy/workspace/codex.config.toml +1 -1
- package/deploy/workspace/entrypoint.sh +20 -79
- package/deploy/workspace/gh-relay +156 -0
- package/deploy/workspace/git-credential-relay +5 -1
- package/dist/bridge/multi-project-client.js +13 -10
- package/dist/bridge/spawner.d.ts +2 -0
- package/dist/bridge/spawner.js +19 -1
- package/dist/bridge/types.d.ts +2 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +115 -69
- package/dist/cloud/api/admin.js +16 -3
- package/dist/cloud/api/codex-auth-helper.js +28 -8
- package/dist/cloud/api/consensus.d.ts +13 -0
- package/dist/cloud/api/consensus.js +259 -0
- package/dist/cloud/api/daemons.js +205 -1
- package/dist/cloud/api/git.js +37 -7
- package/dist/cloud/api/onboarding.js +4 -1
- package/dist/cloud/api/provider-env.d.ts +5 -0
- package/dist/cloud/api/provider-env.js +27 -0
- package/dist/cloud/api/providers.js +2 -0
- package/dist/cloud/api/test-helpers.js +130 -0
- package/dist/cloud/api/workspaces.js +38 -3
- package/dist/cloud/db/bulk-ingest.d.ts +88 -0
- package/dist/cloud/db/bulk-ingest.js +268 -0
- package/dist/cloud/db/drizzle.d.ts +33 -0
- package/dist/cloud/db/drizzle.js +174 -2
- package/dist/cloud/db/index.d.ts +24 -5
- package/dist/cloud/db/index.js +19 -4
- package/dist/cloud/db/schema.d.ts +397 -3
- package/dist/cloud/db/schema.js +75 -1
- package/dist/cloud/provisioner/index.d.ts +8 -0
- package/dist/cloud/provisioner/index.js +256 -50
- package/dist/cloud/server.js +47 -3
- package/dist/cloud/services/index.d.ts +1 -0
- package/dist/cloud/services/index.js +2 -0
- package/dist/cloud/services/nango.d.ts +3 -4
- package/dist/cloud/services/nango.js +11 -33
- package/dist/cloud/services/workspace-keepalive.d.ts +76 -0
- package/dist/cloud/services/workspace-keepalive.js +234 -0
- package/dist/config/relay-config.d.ts +23 -0
- package/dist/config/relay-config.js +23 -0
- package/dist/daemon/agent-manager.d.ts +20 -1
- package/dist/daemon/agent-manager.js +47 -0
- package/dist/daemon/agent-registry.js +4 -4
- package/dist/daemon/agent-signing.d.ts +158 -0
- package/dist/daemon/agent-signing.js +523 -0
- package/dist/daemon/api.js +18 -1
- package/dist/daemon/cli-auth.d.ts +4 -1
- package/dist/daemon/cli-auth.js +55 -11
- package/dist/daemon/cloud-sync.d.ts +47 -1
- package/dist/daemon/cloud-sync.js +152 -3
- package/dist/daemon/connection.d.ts +28 -0
- package/dist/daemon/connection.js +98 -15
- package/dist/daemon/consensus-integration.d.ts +167 -0
- package/dist/daemon/consensus-integration.js +371 -0
- package/dist/daemon/consensus.d.ts +271 -0
- package/dist/daemon/consensus.js +632 -0
- package/dist/daemon/delivery-tracker.d.ts +34 -0
- package/dist/daemon/delivery-tracker.js +104 -0
- package/dist/daemon/enhanced-features.d.ts +118 -0
- package/dist/daemon/enhanced-features.js +178 -0
- package/dist/daemon/index.d.ts +4 -0
- package/dist/daemon/index.js +5 -0
- package/dist/daemon/rate-limiter.d.ts +68 -0
- package/dist/daemon/rate-limiter.js +130 -0
- package/dist/daemon/router.d.ts +18 -11
- package/dist/daemon/router.js +55 -111
- package/dist/daemon/server.d.ts +13 -1
- package/dist/daemon/server.js +71 -9
- package/dist/daemon/sync-queue.d.ts +116 -0
- package/dist/daemon/sync-queue.js +361 -0
- package/dist/health-worker-manager.d.ts +62 -0
- package/dist/health-worker-manager.js +144 -0
- package/dist/health-worker.d.ts +9 -0
- package/dist/health-worker.js +79 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -1
- package/dist/memory/context-compaction.d.ts +156 -0
- package/dist/memory/context-compaction.js +453 -0
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +1 -0
- package/dist/protocol/channels.js +4 -4
- package/dist/protocol/framing.d.ts +72 -10
- package/dist/protocol/framing.js +194 -25
- package/dist/storage/adapter.d.ts +8 -1
- package/dist/storage/adapter.js +11 -0
- package/dist/storage/batched-sqlite-adapter.d.ts +71 -0
- package/dist/storage/batched-sqlite-adapter.js +183 -0
- package/dist/storage/dead-letter-queue.d.ts +196 -0
- package/dist/storage/dead-letter-queue.js +427 -0
- package/dist/storage/dlq-adapter.d.ts +195 -0
- package/dist/storage/dlq-adapter.js +664 -0
- package/dist/trajectory/config.d.ts +32 -14
- package/dist/trajectory/config.js +38 -16
- package/dist/trajectory/integration.js +217 -64
- package/dist/utils/git-remote.d.ts +47 -0
- package/dist/utils/git-remote.js +125 -0
- package/dist/utils/id-generator.d.ts +35 -0
- package/dist/utils/id-generator.js +60 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/precompiled-patterns.d.ts +110 -0
- package/dist/utils/precompiled-patterns.js +322 -0
- package/dist/wrapper/auth-detection.js +1 -1
- package/dist/wrapper/base-wrapper.d.ts +36 -0
- package/dist/wrapper/base-wrapper.js +48 -2
- package/dist/wrapper/client.d.ts +14 -4
- package/dist/wrapper/client.js +84 -31
- package/dist/wrapper/idle-detector.d.ts +102 -0
- package/dist/wrapper/idle-detector.js +279 -0
- package/dist/wrapper/parser.d.ts +4 -0
- package/dist/wrapper/parser.js +19 -1
- package/dist/wrapper/pty-wrapper.d.ts +7 -1
- package/dist/wrapper/pty-wrapper.js +51 -27
- package/dist/wrapper/tmux-wrapper.d.ts +12 -1
- package/dist/wrapper/tmux-wrapper.js +65 -17
- package/package.json +5 -5
- package/scripts/run-migrations.js +43 -0
- package/scripts/verify-schema.js +134 -0
- package/tests/benchmarks/protocol.bench.ts +310 -0
- package/dist/dashboard/out/404.html +0 -1
- package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_buildManifest.js +0 -1
- package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_ssgManifest.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/117-f7b8ab0809342e77.js +0 -2
- package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +0 -9
- package/dist/dashboard/out/_next/static/chunks/648-5cc6e1921389a58a.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/766-b54f0853794b78c3.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/83-b51836037078006c.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/891-6cd50de1224f70bb.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/899-bb19a9b3d9b39ea6.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/_not-found/page-53b8a69f76db17d0.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-8939b0fc700f7eca.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/page-5af1b6b439858aa6.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-f45ecbc3e06134fc.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/history/page-8c8bed33beb2bf1c.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/login/page-16f3b49e55b1e0ed.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-ac39dc0cc3c26fa7.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/page-4a5938c18a11a654.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/pricing/page-982a7000fee44014.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-ac3a6ac433fd6001.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-09f9caae98a18c09.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/signup/page-547dd0ca55ecd0ba.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +0 -18
- package/dist/dashboard/out/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/main-2ee6beb2ae96d210.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/main-app-5d692157a8eb1fd9.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +0 -1
- package/dist/dashboard/out/_next/static/css/85d2af9c7ac74d62.css +0 -1
- package/dist/dashboard/out/_next/static/css/fe4b28883eeff359.css +0 -1
- package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +0 -45
- package/dist/dashboard/out/alt-logos/logo.svg +0 -38
- package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo.svg +0 -38
- package/dist/dashboard/out/app/onboarding.html +0 -1
- package/dist/dashboard/out/app/onboarding.txt +0 -7
- package/dist/dashboard/out/app.html +0 -1
- package/dist/dashboard/out/app.txt +0 -7
- package/dist/dashboard/out/apple-icon.png +0 -0
- package/dist/dashboard/out/connect-repos.html +0 -1
- package/dist/dashboard/out/connect-repos.txt +0 -7
- package/dist/dashboard/out/history.html +0 -1
- package/dist/dashboard/out/history.txt +0 -7
- package/dist/dashboard/out/index.html +0 -1
- package/dist/dashboard/out/index.txt +0 -7
- package/dist/dashboard/out/login.html +0 -6
- package/dist/dashboard/out/login.txt +0 -7
- package/dist/dashboard/out/metrics.html +0 -1
- package/dist/dashboard/out/metrics.txt +0 -7
- package/dist/dashboard/out/pricing.html +0 -13
- package/dist/dashboard/out/pricing.txt +0 -7
- package/dist/dashboard/out/providers/setup/claude.html +0 -1
- package/dist/dashboard/out/providers/setup/claude.txt +0 -8
- package/dist/dashboard/out/providers/setup/codex.html +0 -1
- package/dist/dashboard/out/providers/setup/codex.txt +0 -8
- package/dist/dashboard/out/providers.html +0 -1
- package/dist/dashboard/out/providers.txt +0 -7
- package/dist/dashboard/out/signup.html +0 -6
- package/dist/dashboard/out/signup.txt +0 -7
- package/dist/dashboard-server/metrics.d.ts +0 -105
- package/dist/dashboard-server/metrics.js +0 -193
- package/dist/dashboard-server/needs-attention.d.ts +0 -24
- package/dist/dashboard-server/needs-attention.js +0 -78
- package/dist/dashboard-server/server.d.ts +0 -15
- package/dist/dashboard-server/server.js +0 -3776
- package/dist/dashboard-server/start.d.ts +0 -6
- package/dist/dashboard-server/start.js +0 -13
- package/dist/dashboard-server/user-bridge.d.ts +0 -103
- package/dist/dashboard-server/user-bridge.js +0 -189
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimized Cloud Sync Queue
|
|
3
|
+
*
|
|
4
|
+
* Handles batched, compressed, resilient message syncing to cloud.
|
|
5
|
+
* Features:
|
|
6
|
+
* - Adaptive batching (size/time/bytes triggers)
|
|
7
|
+
* - Gzip compression for payloads over threshold
|
|
8
|
+
* - Disk spillover for offline resilience
|
|
9
|
+
* - Retry with exponential backoff
|
|
10
|
+
* - Startup reconciliation
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'node:fs/promises';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import * as os from 'node:os';
|
|
15
|
+
import { gzip } from 'node:zlib';
|
|
16
|
+
import { promisify } from 'node:util';
|
|
17
|
+
import { generateId } from '../utils/id-generator.js';
|
|
18
|
+
import { createLogger } from '../utils/logger.js';
|
|
19
|
+
const gzipAsync = promisify(gzip);
|
|
20
|
+
const log = createLogger('sync-queue');
|
|
21
|
+
export const DEFAULT_SYNC_QUEUE_CONFIG = {
|
|
22
|
+
cloudUrl: 'https://agent-relay.com',
|
|
23
|
+
apiKey: '',
|
|
24
|
+
batchSize: 100,
|
|
25
|
+
batchDelayMs: 200,
|
|
26
|
+
maxBatchBytes: 512 * 1024, // 512KB
|
|
27
|
+
compressionThreshold: 1024, // 1KB
|
|
28
|
+
spillDir: path.join(os.tmpdir(), 'agent-relay-sync'),
|
|
29
|
+
maxSpillFiles: 100,
|
|
30
|
+
maxRetries: 3,
|
|
31
|
+
retryDelayMs: 1000,
|
|
32
|
+
verbose: false,
|
|
33
|
+
};
|
|
34
|
+
export class SyncQueue {
|
|
35
|
+
config;
|
|
36
|
+
queue = [];
|
|
37
|
+
queueBytes = 0;
|
|
38
|
+
flushTimer;
|
|
39
|
+
flushing = false;
|
|
40
|
+
flushPromise;
|
|
41
|
+
// Stats
|
|
42
|
+
stats = {
|
|
43
|
+
queuedMessages: 0,
|
|
44
|
+
queuedBytes: 0,
|
|
45
|
+
totalSynced: 0,
|
|
46
|
+
totalFailed: 0,
|
|
47
|
+
totalCompressed: 0,
|
|
48
|
+
totalBytesTransferred: 0,
|
|
49
|
+
spilledFiles: 0,
|
|
50
|
+
};
|
|
51
|
+
constructor(config) {
|
|
52
|
+
this.config = { ...DEFAULT_SYNC_QUEUE_CONFIG, ...config };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Queue a message for sync to cloud.
|
|
56
|
+
* May trigger an immediate flush if thresholds are exceeded.
|
|
57
|
+
*/
|
|
58
|
+
async enqueue(message) {
|
|
59
|
+
// Wait for any in-progress flush
|
|
60
|
+
if (this.flushPromise) {
|
|
61
|
+
await this.flushPromise;
|
|
62
|
+
}
|
|
63
|
+
const msgJson = JSON.stringify(message);
|
|
64
|
+
const sizeBytes = Buffer.byteLength(msgJson, 'utf-8');
|
|
65
|
+
this.queue.push({ message, sizeBytes });
|
|
66
|
+
this.queueBytes += sizeBytes;
|
|
67
|
+
this.stats.queuedMessages = this.queue.length;
|
|
68
|
+
this.stats.queuedBytes = this.queueBytes;
|
|
69
|
+
// Check flush triggers
|
|
70
|
+
const shouldFlush = this.queue.length >= this.config.batchSize ||
|
|
71
|
+
this.queueBytes >= this.config.maxBatchBytes;
|
|
72
|
+
if (shouldFlush) {
|
|
73
|
+
await this.flush();
|
|
74
|
+
}
|
|
75
|
+
else if (!this.flushTimer) {
|
|
76
|
+
this.flushTimer = setTimeout(() => {
|
|
77
|
+
this.flush().catch((err) => {
|
|
78
|
+
log.error('Timer flush failed', { error: String(err) });
|
|
79
|
+
});
|
|
80
|
+
}, this.config.batchDelayMs);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Enqueue multiple messages at once.
|
|
85
|
+
*/
|
|
86
|
+
async enqueueBatch(messages) {
|
|
87
|
+
for (const msg of messages) {
|
|
88
|
+
await this.enqueue(msg);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Flush all queued messages to cloud.
|
|
93
|
+
*/
|
|
94
|
+
async flush() {
|
|
95
|
+
// Clear timer
|
|
96
|
+
if (this.flushTimer) {
|
|
97
|
+
clearTimeout(this.flushTimer);
|
|
98
|
+
this.flushTimer = undefined;
|
|
99
|
+
}
|
|
100
|
+
// Skip if empty or already flushing
|
|
101
|
+
if (this.queue.length === 0 || this.flushing) {
|
|
102
|
+
return { synced: 0, duplicates: 0, failed: 0, compressed: false, bytesTransferred: 0 };
|
|
103
|
+
}
|
|
104
|
+
this.flushing = true;
|
|
105
|
+
// Take current batch
|
|
106
|
+
const batch = this.queue;
|
|
107
|
+
const batchBytes = this.queueBytes;
|
|
108
|
+
this.queue = [];
|
|
109
|
+
this.queueBytes = 0;
|
|
110
|
+
this.stats.queuedMessages = 0;
|
|
111
|
+
this.stats.queuedBytes = 0;
|
|
112
|
+
this.flushPromise = this.syncBatch(batch, batchBytes);
|
|
113
|
+
try {
|
|
114
|
+
const result = await this.flushPromise;
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
this.flushing = false;
|
|
119
|
+
this.flushPromise = undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Sync a batch of messages to cloud with retry and spillover.
|
|
124
|
+
*/
|
|
125
|
+
async syncBatch(batch, batchBytes) {
|
|
126
|
+
const messages = batch.map((q) => q.message);
|
|
127
|
+
let lastError = null;
|
|
128
|
+
for (let attempt = 0; attempt < this.config.maxRetries; attempt++) {
|
|
129
|
+
try {
|
|
130
|
+
const result = await this.sendToCloud(messages, batchBytes);
|
|
131
|
+
this.stats.totalSynced += result.synced;
|
|
132
|
+
this.stats.totalBytesTransferred += result.bytesTransferred;
|
|
133
|
+
if (result.compressed) {
|
|
134
|
+
this.stats.totalCompressed++;
|
|
135
|
+
}
|
|
136
|
+
this.stats.lastSyncAt = Date.now();
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
lastError = err;
|
|
141
|
+
this.stats.lastError = lastError.message;
|
|
142
|
+
if (attempt < this.config.maxRetries - 1) {
|
|
143
|
+
// Exponential backoff
|
|
144
|
+
const delay = this.config.retryDelayMs * Math.pow(2, attempt);
|
|
145
|
+
if (this.config.verbose) {
|
|
146
|
+
log.warn(`Sync attempt ${attempt + 1} failed, retrying in ${delay}ms`, {
|
|
147
|
+
error: lastError.message,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
await this.sleep(delay);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// All retries failed - spill to disk
|
|
155
|
+
log.error('Sync failed after retries, spilling to disk', {
|
|
156
|
+
count: messages.length,
|
|
157
|
+
error: lastError?.message,
|
|
158
|
+
});
|
|
159
|
+
await this.spillToDisk(messages);
|
|
160
|
+
this.stats.totalFailed += messages.length;
|
|
161
|
+
return {
|
|
162
|
+
synced: 0,
|
|
163
|
+
duplicates: 0,
|
|
164
|
+
failed: messages.length,
|
|
165
|
+
compressed: false,
|
|
166
|
+
bytesTransferred: 0,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Send messages to cloud API with optional compression.
|
|
171
|
+
*/
|
|
172
|
+
async sendToCloud(messages, _estimatedBytes) {
|
|
173
|
+
// Transform to API format
|
|
174
|
+
const syncPayload = {
|
|
175
|
+
messages: messages.map((msg) => ({
|
|
176
|
+
id: msg.id,
|
|
177
|
+
ts: msg.ts,
|
|
178
|
+
from: msg.from,
|
|
179
|
+
to: msg.to,
|
|
180
|
+
body: msg.body,
|
|
181
|
+
kind: msg.kind,
|
|
182
|
+
topic: msg.topic,
|
|
183
|
+
thread: msg.thread,
|
|
184
|
+
is_broadcast: msg.is_broadcast,
|
|
185
|
+
is_urgent: msg.is_urgent,
|
|
186
|
+
data: msg.data,
|
|
187
|
+
payload_meta: msg.payloadMeta,
|
|
188
|
+
})),
|
|
189
|
+
};
|
|
190
|
+
const payloadJson = JSON.stringify(syncPayload);
|
|
191
|
+
const payloadBytes = Buffer.byteLength(payloadJson, 'utf-8');
|
|
192
|
+
// Determine if we should compress
|
|
193
|
+
const shouldCompress = payloadBytes > this.config.compressionThreshold;
|
|
194
|
+
let body;
|
|
195
|
+
let contentEncoding;
|
|
196
|
+
if (shouldCompress) {
|
|
197
|
+
body = await gzipAsync(Buffer.from(payloadJson));
|
|
198
|
+
contentEncoding = 'gzip';
|
|
199
|
+
if (this.config.verbose) {
|
|
200
|
+
const ratio = ((1 - body.length / payloadBytes) * 100).toFixed(1);
|
|
201
|
+
log.info(`Compressed ${payloadBytes} → ${body.length} bytes (${ratio}% reduction)`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
body = payloadJson;
|
|
206
|
+
}
|
|
207
|
+
const headers = {
|
|
208
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
209
|
+
'Content-Type': 'application/json',
|
|
210
|
+
};
|
|
211
|
+
if (contentEncoding) {
|
|
212
|
+
headers['Content-Encoding'] = contentEncoding;
|
|
213
|
+
}
|
|
214
|
+
const response = await fetch(`${this.config.cloudUrl}/api/daemons/messages/sync`, {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
headers,
|
|
217
|
+
body,
|
|
218
|
+
});
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
const errorText = await response.text();
|
|
221
|
+
throw new Error(`Sync failed: ${response.status} - ${errorText}`);
|
|
222
|
+
}
|
|
223
|
+
const result = (await response.json());
|
|
224
|
+
return {
|
|
225
|
+
synced: result.synced,
|
|
226
|
+
duplicates: result.duplicates,
|
|
227
|
+
failed: 0,
|
|
228
|
+
compressed: shouldCompress,
|
|
229
|
+
bytesTransferred: typeof body === 'string' ? Buffer.byteLength(body) : body.length,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Spill failed batch to disk for later recovery.
|
|
234
|
+
*/
|
|
235
|
+
async spillToDisk(messages) {
|
|
236
|
+
try {
|
|
237
|
+
await fs.mkdir(this.config.spillDir, { recursive: true });
|
|
238
|
+
const filename = `spill-${Date.now()}-${generateId().replace(/-/g, '')}.json`;
|
|
239
|
+
const filepath = path.join(this.config.spillDir, filename);
|
|
240
|
+
await fs.writeFile(filepath, JSON.stringify(messages));
|
|
241
|
+
this.stats.spilledFiles++;
|
|
242
|
+
if (this.config.verbose) {
|
|
243
|
+
log.info(`Spilled ${messages.length} messages to ${filename}`);
|
|
244
|
+
}
|
|
245
|
+
// Cleanup old spill files
|
|
246
|
+
await this.cleanupSpillFiles();
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
log.error('Failed to spill to disk', { error: String(err) });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Recover and sync messages from spill files.
|
|
254
|
+
* Call this on startup to resume failed syncs.
|
|
255
|
+
*/
|
|
256
|
+
async recoverSpilledMessages() {
|
|
257
|
+
let recovered = 0;
|
|
258
|
+
let failed = 0;
|
|
259
|
+
try {
|
|
260
|
+
const files = await fs.readdir(this.config.spillDir);
|
|
261
|
+
const spillFiles = files.filter((f) => f.startsWith('spill-')).sort();
|
|
262
|
+
for (const file of spillFiles) {
|
|
263
|
+
const filepath = path.join(this.config.spillDir, file);
|
|
264
|
+
try {
|
|
265
|
+
const content = await fs.readFile(filepath, 'utf-8');
|
|
266
|
+
const messages = JSON.parse(content);
|
|
267
|
+
// Try to sync
|
|
268
|
+
const result = await this.sendToCloud(messages, Buffer.byteLength(content, 'utf-8'));
|
|
269
|
+
if (result.synced > 0 || result.duplicates > 0) {
|
|
270
|
+
// Success - remove spill file
|
|
271
|
+
await fs.unlink(filepath);
|
|
272
|
+
recovered += messages.length;
|
|
273
|
+
this.stats.spilledFiles = Math.max(0, this.stats.spilledFiles - 1);
|
|
274
|
+
if (this.config.verbose) {
|
|
275
|
+
log.info(`Recovered ${messages.length} messages from ${file}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
failed += messages.length;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
log.warn(`Failed to recover ${file}`, { error: String(err) });
|
|
284
|
+
failed++;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
// Directory doesn't exist or other error
|
|
290
|
+
if (err.code !== 'ENOENT') {
|
|
291
|
+
log.error('Failed to scan spill directory', { error: String(err) });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (recovered > 0) {
|
|
295
|
+
log.info(`Recovered ${recovered} messages from spill files`);
|
|
296
|
+
}
|
|
297
|
+
return { recovered, failed };
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Cleanup old spill files beyond the limit.
|
|
301
|
+
*/
|
|
302
|
+
async cleanupSpillFiles() {
|
|
303
|
+
try {
|
|
304
|
+
const files = await fs.readdir(this.config.spillDir);
|
|
305
|
+
const spillFiles = files.filter((f) => f.startsWith('spill-')).sort();
|
|
306
|
+
if (spillFiles.length > this.config.maxSpillFiles) {
|
|
307
|
+
const toDelete = spillFiles.slice(0, spillFiles.length - this.config.maxSpillFiles);
|
|
308
|
+
for (const file of toDelete) {
|
|
309
|
+
await fs.unlink(path.join(this.config.spillDir, file)).catch(() => { });
|
|
310
|
+
}
|
|
311
|
+
if (this.config.verbose) {
|
|
312
|
+
log.info(`Cleaned up ${toDelete.length} old spill files`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
// Ignore cleanup errors
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get sync queue statistics.
|
|
322
|
+
*/
|
|
323
|
+
getStats() {
|
|
324
|
+
return { ...this.stats };
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Reset statistics (for testing or periodic reporting).
|
|
328
|
+
*/
|
|
329
|
+
resetStats() {
|
|
330
|
+
this.stats = {
|
|
331
|
+
queuedMessages: this.queue.length,
|
|
332
|
+
queuedBytes: this.queueBytes,
|
|
333
|
+
totalSynced: 0,
|
|
334
|
+
totalFailed: 0,
|
|
335
|
+
totalCompressed: 0,
|
|
336
|
+
totalBytesTransferred: 0,
|
|
337
|
+
spilledFiles: this.stats.spilledFiles, // Preserve spill count
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Gracefully close the queue, flushing any pending messages.
|
|
342
|
+
*/
|
|
343
|
+
async close() {
|
|
344
|
+
if (this.flushTimer) {
|
|
345
|
+
clearTimeout(this.flushTimer);
|
|
346
|
+
this.flushTimer = undefined;
|
|
347
|
+
}
|
|
348
|
+
// Wait for any in-progress flush
|
|
349
|
+
if (this.flushPromise) {
|
|
350
|
+
await this.flushPromise;
|
|
351
|
+
}
|
|
352
|
+
// Flush remaining
|
|
353
|
+
if (this.queue.length > 0) {
|
|
354
|
+
await this.flush();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
sleep(ms) {
|
|
358
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
//# sourceMappingURL=sync-queue.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Worker Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the health check worker thread, sending periodic stats updates
|
|
5
|
+
* and handling worker lifecycle.
|
|
6
|
+
*/
|
|
7
|
+
export interface HealthWorkerConfig {
|
|
8
|
+
/** Port for health check server (default: main port + 1) */
|
|
9
|
+
port: number;
|
|
10
|
+
/** Interval for sending stats updates (default: 5000ms) */
|
|
11
|
+
statsInterval?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface HealthStatsProvider {
|
|
14
|
+
getUptime: () => number;
|
|
15
|
+
getMemoryMB: () => number;
|
|
16
|
+
getRelayConnected: () => boolean;
|
|
17
|
+
getAgentCount: () => number;
|
|
18
|
+
getStatus: () => 'healthy' | 'busy' | 'degraded';
|
|
19
|
+
}
|
|
20
|
+
export declare class HealthWorkerManager {
|
|
21
|
+
private worker;
|
|
22
|
+
private statsInterval;
|
|
23
|
+
private config;
|
|
24
|
+
private statsProvider;
|
|
25
|
+
private ready;
|
|
26
|
+
constructor(config: HealthWorkerConfig, statsProvider: HealthStatsProvider);
|
|
27
|
+
/**
|
|
28
|
+
* Start the health worker thread
|
|
29
|
+
*/
|
|
30
|
+
start(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Stop the health worker thread
|
|
33
|
+
*/
|
|
34
|
+
stop(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Check if worker is ready
|
|
37
|
+
*/
|
|
38
|
+
isReady(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Get the port the health worker is listening on
|
|
41
|
+
*/
|
|
42
|
+
getPort(): number;
|
|
43
|
+
/**
|
|
44
|
+
* Start periodic stats updates to worker
|
|
45
|
+
*/
|
|
46
|
+
private startStatsUpdates;
|
|
47
|
+
/**
|
|
48
|
+
* Stop stats updates
|
|
49
|
+
*/
|
|
50
|
+
private stopStatsUpdates;
|
|
51
|
+
/**
|
|
52
|
+
* Send current stats to worker
|
|
53
|
+
*/
|
|
54
|
+
private sendStats;
|
|
55
|
+
}
|
|
56
|
+
/** Default health port offset from main port */
|
|
57
|
+
export declare const HEALTH_PORT_OFFSET = 1;
|
|
58
|
+
/**
|
|
59
|
+
* Calculate health port from main port
|
|
60
|
+
*/
|
|
61
|
+
export declare function getHealthPort(mainPort: number): number;
|
|
62
|
+
//# sourceMappingURL=health-worker-manager.d.ts.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Worker Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the health check worker thread, sending periodic stats updates
|
|
5
|
+
* and handling worker lifecycle.
|
|
6
|
+
*/
|
|
7
|
+
import { Worker } from 'node:worker_threads';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
export class HealthWorkerManager {
|
|
13
|
+
worker = null;
|
|
14
|
+
statsInterval = null;
|
|
15
|
+
config;
|
|
16
|
+
statsProvider;
|
|
17
|
+
ready = false;
|
|
18
|
+
constructor(config, statsProvider) {
|
|
19
|
+
this.config = {
|
|
20
|
+
statsInterval: 5000,
|
|
21
|
+
...config,
|
|
22
|
+
};
|
|
23
|
+
this.statsProvider = statsProvider;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Start the health worker thread
|
|
27
|
+
*/
|
|
28
|
+
async start() {
|
|
29
|
+
if (this.worker) {
|
|
30
|
+
console.warn('[health-manager] Worker already running');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
// Worker script path - handle both dev (src) and prod (dist)
|
|
35
|
+
const workerPath = path.join(__dirname, 'health-worker.js');
|
|
36
|
+
this.worker = new Worker(workerPath, {
|
|
37
|
+
workerData: { port: this.config.port },
|
|
38
|
+
});
|
|
39
|
+
this.worker.on('message', (msg) => {
|
|
40
|
+
if (msg.type === 'ready') {
|
|
41
|
+
this.ready = true;
|
|
42
|
+
console.log(`[health-manager] Worker ready on port ${msg.port}`);
|
|
43
|
+
this.startStatsUpdates();
|
|
44
|
+
resolve();
|
|
45
|
+
}
|
|
46
|
+
else if (msg.type === 'error') {
|
|
47
|
+
console.error('[health-manager] Worker error:', msg.error);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
this.worker.on('error', (err) => {
|
|
51
|
+
console.error('[health-manager] Worker thread error:', err);
|
|
52
|
+
if (!this.ready) {
|
|
53
|
+
reject(err);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.worker.on('exit', (code) => {
|
|
57
|
+
console.log(`[health-manager] Worker exited with code ${code}`);
|
|
58
|
+
this.ready = false;
|
|
59
|
+
this.worker = null;
|
|
60
|
+
this.stopStatsUpdates();
|
|
61
|
+
});
|
|
62
|
+
// Timeout for worker startup
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
if (!this.ready) {
|
|
65
|
+
reject(new Error('Health worker startup timeout'));
|
|
66
|
+
}
|
|
67
|
+
}, 10000);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Stop the health worker thread
|
|
72
|
+
*/
|
|
73
|
+
async stop() {
|
|
74
|
+
this.stopStatsUpdates();
|
|
75
|
+
if (this.worker) {
|
|
76
|
+
await this.worker.terminate();
|
|
77
|
+
this.worker = null;
|
|
78
|
+
this.ready = false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Check if worker is ready
|
|
83
|
+
*/
|
|
84
|
+
isReady() {
|
|
85
|
+
return this.ready;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get the port the health worker is listening on
|
|
89
|
+
*/
|
|
90
|
+
getPort() {
|
|
91
|
+
return this.config.port;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Start periodic stats updates to worker
|
|
95
|
+
*/
|
|
96
|
+
startStatsUpdates() {
|
|
97
|
+
if (this.statsInterval)
|
|
98
|
+
return;
|
|
99
|
+
// Send initial stats
|
|
100
|
+
this.sendStats();
|
|
101
|
+
// Send periodic updates
|
|
102
|
+
this.statsInterval = setInterval(() => {
|
|
103
|
+
this.sendStats();
|
|
104
|
+
}, this.config.statsInterval);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Stop stats updates
|
|
108
|
+
*/
|
|
109
|
+
stopStatsUpdates() {
|
|
110
|
+
if (this.statsInterval) {
|
|
111
|
+
clearInterval(this.statsInterval);
|
|
112
|
+
this.statsInterval = null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Send current stats to worker
|
|
117
|
+
*/
|
|
118
|
+
sendStats() {
|
|
119
|
+
if (!this.worker || !this.ready)
|
|
120
|
+
return;
|
|
121
|
+
try {
|
|
122
|
+
const stats = {
|
|
123
|
+
uptime: this.statsProvider.getUptime(),
|
|
124
|
+
memoryMB: this.statsProvider.getMemoryMB(),
|
|
125
|
+
relayConnected: this.statsProvider.getRelayConnected(),
|
|
126
|
+
agentCount: this.statsProvider.getAgentCount(),
|
|
127
|
+
status: this.statsProvider.getStatus(),
|
|
128
|
+
};
|
|
129
|
+
this.worker.postMessage(stats);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
console.error('[health-manager] Failed to send stats:', err);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** Default health port offset from main port */
|
|
137
|
+
export const HEALTH_PORT_OFFSET = 1;
|
|
138
|
+
/**
|
|
139
|
+
* Calculate health port from main port
|
|
140
|
+
*/
|
|
141
|
+
export function getHealthPort(mainPort) {
|
|
142
|
+
return mainPort + HEALTH_PORT_OFFSET;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=health-worker-manager.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Check Worker Thread
|
|
3
|
+
*
|
|
4
|
+
* Runs a minimal HTTP server on a separate thread to handle health checks.
|
|
5
|
+
* This ensures health checks respond even when the main event loop is blocked
|
|
6
|
+
* by heavy compute tasks (builds, large file operations, etc.).
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=health-worker.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Check Worker Thread
|
|
3
|
+
*
|
|
4
|
+
* Runs a minimal HTTP server on a separate thread to handle health checks.
|
|
5
|
+
* This ensures health checks respond even when the main event loop is blocked
|
|
6
|
+
* by heavy compute tasks (builds, large file operations, etc.).
|
|
7
|
+
*/
|
|
8
|
+
import { parentPort, workerData } from 'node:worker_threads';
|
|
9
|
+
import http from 'node:http';
|
|
10
|
+
// Default stats until we receive updates from main thread
|
|
11
|
+
let currentStats = {
|
|
12
|
+
uptime: 0,
|
|
13
|
+
memoryMB: 0,
|
|
14
|
+
relayConnected: false,
|
|
15
|
+
agentCount: 0,
|
|
16
|
+
status: 'healthy',
|
|
17
|
+
};
|
|
18
|
+
// Track when we last received stats from main thread
|
|
19
|
+
let lastStatsUpdate = Date.now();
|
|
20
|
+
const STATS_STALE_THRESHOLD_MS = 60_000; // 1 minute
|
|
21
|
+
// Listen for stats updates from main thread
|
|
22
|
+
if (parentPort) {
|
|
23
|
+
parentPort.on('message', (stats) => {
|
|
24
|
+
currentStats = stats;
|
|
25
|
+
lastStatsUpdate = Date.now();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const { port } = workerData;
|
|
29
|
+
const server = http.createServer((req, res) => {
|
|
30
|
+
// Only handle /health endpoint
|
|
31
|
+
if (req.url !== '/health' && req.url !== '/') {
|
|
32
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
33
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Check if stats are stale (main thread might be blocked)
|
|
37
|
+
const statsAge = Date.now() - lastStatsUpdate;
|
|
38
|
+
const isStale = statsAge > STATS_STALE_THRESHOLD_MS;
|
|
39
|
+
// Determine status
|
|
40
|
+
let status = currentStats.status;
|
|
41
|
+
if (isStale) {
|
|
42
|
+
status = 'busy'; // Main thread is likely blocked
|
|
43
|
+
}
|
|
44
|
+
const response = {
|
|
45
|
+
status,
|
|
46
|
+
uptime: currentStats.uptime,
|
|
47
|
+
memoryMB: currentStats.memoryMB,
|
|
48
|
+
relayConnected: currentStats.relayConnected,
|
|
49
|
+
agentCount: currentStats.agentCount,
|
|
50
|
+
statsAgeMs: statsAge,
|
|
51
|
+
worker: true, // Indicates response is from worker thread
|
|
52
|
+
};
|
|
53
|
+
// Return 200 for healthy/busy, 503 for degraded
|
|
54
|
+
const statusCode = status === 'degraded' ? 503 : 200;
|
|
55
|
+
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
56
|
+
res.end(JSON.stringify(response));
|
|
57
|
+
});
|
|
58
|
+
server.listen(port, '0.0.0.0', () => {
|
|
59
|
+
console.log(`[health-worker] Health check server listening on port ${port}`);
|
|
60
|
+
// Notify main thread we're ready
|
|
61
|
+
if (parentPort) {
|
|
62
|
+
parentPort.postMessage({ type: 'ready', port });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
// Handle errors gracefully
|
|
66
|
+
server.on('error', (err) => {
|
|
67
|
+
console.error('[health-worker] Server error:', err);
|
|
68
|
+
if (parentPort) {
|
|
69
|
+
parentPort.postMessage({ type: 'error', error: String(err) });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
// Keep the worker alive
|
|
73
|
+
process.on('uncaughtException', (err) => {
|
|
74
|
+
console.error('[health-worker] Uncaught exception:', err);
|
|
75
|
+
});
|
|
76
|
+
process.on('unhandledRejection', (err) => {
|
|
77
|
+
console.error('[health-worker] Unhandled rejection:', err);
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=health-worker.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,6 @@ export * from './wrapper/index.js';
|
|
|
8
8
|
export * from './utils/index.js';
|
|
9
9
|
export * from './hooks/index.js';
|
|
10
10
|
export { type StoredMessage, type MessageQuery, type StorageAdapter, type StorageConfig, } from './storage/adapter.js';
|
|
11
|
-
export { type MemoryAdapter, type MemoryEntry, type MemoryConfig, type MemoryService, type MemorySearchQuery, type AddMemoryOptions, type MemoryResult, createMemoryAdapter, createMemoryService, createMemoryHooks, getMemoryHooks, InMemoryAdapter, SupermemoryAdapter, } from './memory/index.js';
|
|
11
|
+
export { type MemoryAdapter, type MemoryEntry, type MemoryConfig, type MemoryService, type MemorySearchQuery, type AddMemoryOptions, type MemoryResult, createMemoryAdapter, createMemoryService, createMemoryHooks, getMemoryHooks, InMemoryAdapter, SupermemoryAdapter, ContextCompactor, createContextCompactor, estimateTokens, estimateContextTokens, type CompactionConfig, type CompactionResult, } from './memory/index.js';
|
|
12
|
+
export { type DLQStorageAdapter, type DeadLetter, type DLQConfig, type DLQStats, type DLQQuery, SQLiteDLQAdapter, PostgresDLQAdapter, InMemoryDLQAdapter, createDLQAdapter, DEFAULT_DLQ_CONFIG, } from './storage/dlq-adapter.js';
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -8,5 +8,9 @@ export * from './wrapper/index.js';
|
|
|
8
8
|
export * from './utils/index.js';
|
|
9
9
|
export * from './hooks/index.js';
|
|
10
10
|
// Memory types and adapters for external consumers
|
|
11
|
-
export { createMemoryAdapter, createMemoryService, createMemoryHooks, getMemoryHooks, InMemoryAdapter, SupermemoryAdapter,
|
|
11
|
+
export { createMemoryAdapter, createMemoryService, createMemoryHooks, getMemoryHooks, InMemoryAdapter, SupermemoryAdapter,
|
|
12
|
+
// Context compaction
|
|
13
|
+
ContextCompactor, createContextCompactor, estimateTokens, estimateContextTokens, } from './memory/index.js';
|
|
14
|
+
// Dead Letter Queue adapters
|
|
15
|
+
export { SQLiteDLQAdapter, PostgresDLQAdapter, InMemoryDLQAdapter, createDLQAdapter, DEFAULT_DLQ_CONFIG, } from './storage/dlq-adapter.js';
|
|
12
16
|
//# sourceMappingURL=index.js.map
|