pi-harness-runtime 1.1.396 → 1.1.398
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/AGENTS.md +54 -7
- package/harness/notification-events.ts +6 -6
- package/index.ts +1 -1
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/logging/README.md +41 -6
- package/packages/logging/dist/classification.d.ts +65 -0
- package/packages/logging/dist/classification.d.ts.map +1 -0
- package/packages/logging/dist/classification.js +155 -0
- package/packages/logging/dist/classification.js.map +1 -0
- package/packages/logging/dist/config.d.ts +9 -0
- package/packages/logging/dist/config.d.ts.map +1 -1
- package/packages/logging/dist/config.js +40 -3
- package/packages/logging/dist/config.js.map +1 -1
- package/packages/logging/dist/index.d.ts +9 -1
- package/packages/logging/dist/index.d.ts.map +1 -1
- package/packages/logging/dist/index.js +10 -1
- package/packages/logging/dist/index.js.map +1 -1
- package/packages/logging/dist/ingest/schema.d.ts +60 -0
- package/packages/logging/dist/ingest/schema.d.ts.map +1 -0
- package/packages/logging/dist/ingest/schema.js +245 -0
- package/packages/logging/dist/ingest/schema.js.map +1 -0
- package/packages/logging/dist/installation.d.ts +32 -0
- package/packages/logging/dist/installation.d.ts.map +1 -0
- package/packages/logging/dist/installation.js +64 -0
- package/packages/logging/dist/installation.js.map +1 -0
- package/packages/logging/dist/sensitive-keys.d.ts +12 -0
- package/packages/logging/dist/sensitive-keys.d.ts.map +1 -0
- package/packages/logging/dist/sensitive-keys.js +39 -0
- package/packages/logging/dist/sensitive-keys.js.map +1 -0
- package/packages/logging/dist/server/credentials.d.ts +85 -0
- package/packages/logging/dist/server/credentials.d.ts.map +1 -0
- package/packages/logging/dist/server/credentials.js +147 -0
- package/packages/logging/dist/server/credentials.js.map +1 -0
- package/packages/logging/dist/server/http.d.ts +36 -0
- package/packages/logging/dist/server/http.d.ts.map +1 -0
- package/packages/logging/dist/server/http.js +139 -0
- package/packages/logging/dist/server/http.js.map +1 -0
- package/packages/logging/dist/server/index-store.d.ts +95 -0
- package/packages/logging/dist/server/index-store.d.ts.map +1 -0
- package/packages/logging/dist/server/index-store.js +190 -0
- package/packages/logging/dist/server/index-store.js.map +1 -0
- package/packages/logging/dist/server/index.d.ts +22 -0
- package/packages/logging/dist/server/index.d.ts.map +1 -0
- package/packages/logging/dist/server/index.js +15 -0
- package/packages/logging/dist/server/index.js.map +1 -0
- package/packages/logging/dist/server/keys.d.ts +22 -0
- package/packages/logging/dist/server/keys.d.ts.map +1 -0
- package/packages/logging/dist/server/keys.js +24 -0
- package/packages/logging/dist/server/keys.js.map +1 -0
- package/packages/logging/dist/server/outbox-worker.d.ts +55 -0
- package/packages/logging/dist/server/outbox-worker.d.ts.map +1 -0
- package/packages/logging/dist/server/outbox-worker.js +95 -0
- package/packages/logging/dist/server/outbox-worker.js.map +1 -0
- package/packages/logging/dist/server/outbox.d.ts +112 -0
- package/packages/logging/dist/server/outbox.d.ts.map +1 -0
- package/packages/logging/dist/server/outbox.js +247 -0
- package/packages/logging/dist/server/outbox.js.map +1 -0
- package/packages/logging/dist/server/service.d.ts +83 -0
- package/packages/logging/dist/server/service.d.ts.map +1 -0
- package/packages/logging/dist/server/service.js +144 -0
- package/packages/logging/dist/server/service.js.map +1 -0
- package/packages/logging/dist/shipper.d.ts +20 -4
- package/packages/logging/dist/shipper.d.ts.map +1 -1
- package/packages/logging/dist/shipper.js +31 -5
- package/packages/logging/dist/shipper.js.map +1 -1
- package/packages/logging/dist/singleton.d.ts.map +1 -1
- package/packages/logging/dist/singleton.js +3 -0
- package/packages/logging/dist/singleton.js.map +1 -1
- package/packages/logging/dist/types.d.ts +28 -1
- package/packages/logging/dist/types.d.ts.map +1 -1
- package/packages/logging/dist/types.js +8 -0
- package/packages/logging/dist/types.js.map +1 -1
- package/packages/logging/package.json +1 -1
- package/packages/logging/src/classification.ts +175 -0
- package/packages/logging/src/config.ts +60 -3
- package/packages/logging/src/index.ts +45 -1
- package/packages/logging/src/ingest/schema.ts +295 -0
- package/packages/logging/src/installation.ts +76 -0
- package/packages/logging/src/sensitive-keys.ts +40 -0
- package/packages/logging/src/server/credentials.ts +198 -0
- package/packages/logging/src/server/http.ts +170 -0
- package/packages/logging/src/server/index-store.ts +242 -0
- package/packages/logging/src/server/index.ts +56 -0
- package/packages/logging/src/server/keys.ts +32 -0
- package/packages/logging/src/server/outbox-worker.ts +124 -0
- package/packages/logging/src/server/outbox.ts +316 -0
- package/packages/logging/src/server/service.ts +196 -0
- package/packages/logging/src/shipper.ts +54 -8
- package/packages/logging/src/singleton.ts +3 -0
- package/packages/logging/src/types.ts +29 -1
- package/packages/logging/test/classification.test.ts +137 -0
- package/packages/logging/test/identity-transport.test.ts +197 -0
- package/packages/logging/test/ingest-schema.test.ts +184 -0
- package/packages/logging/test/integration.test.ts +9 -4
- package/packages/logging/test/log-http.test.ts +246 -0
- package/packages/logging/test/server.test.ts +444 -0
- package/packages/logging/test/shipper.test.ts +11 -11
- package/packages/logging/test/spool.test.ts +4 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/dist/adapters/detail-format.d.ts +34 -0
- package/packages/notification/dist/adapters/detail-format.d.ts.map +1 -0
- package/packages/notification/dist/adapters/detail-format.js +55 -0
- package/packages/notification/dist/adapters/detail-format.js.map +1 -0
- package/packages/notification/dist/adapters/email-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/email-adapter.js +3 -2
- package/packages/notification/dist/adapters/email-adapter.js.map +1 -1
- package/packages/notification/dist/adapters/line-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/line-adapter.js +3 -2
- package/packages/notification/dist/adapters/line-adapter.js.map +1 -1
- package/packages/notification/dist/adapters/ntfy-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/ntfy-adapter.js +3 -2
- package/packages/notification/dist/adapters/ntfy-adapter.js.map +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.d.ts +14 -0
- package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.js +83 -67
- package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
- package/packages/notification/dist/notification-center.d.ts.map +1 -1
- package/packages/notification/dist/notification-center.js +1 -0
- package/packages/notification/dist/notification-center.js.map +1 -1
- package/packages/notification/dist/types.d.ts +3 -0
- package/packages/notification/dist/types.d.ts.map +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/notification/src/adapters/detail-format.ts +74 -0
- package/packages/notification/src/adapters/email-adapter.ts +3 -2
- package/packages/notification/src/adapters/line-adapter.ts +3 -2
- package/packages/notification/src/adapters/ntfy-adapter.ts +3 -2
- package/packages/notification/src/adapters/telegram-adapter.ts +92 -74
- package/packages/notification/src/notification-center.ts +1 -0
- package/packages/notification/src/types.ts +3 -0
- package/packages/notification/test/detail-format.test.ts +106 -0
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/scripts/unified-webhook-server.ts +86 -81
|
@@ -30,8 +30,18 @@ import type { RelayServerOptions } from "../packages/relay/src/server";
|
|
|
30
30
|
// chat id) - the harness and this server must agree on who may write in.
|
|
31
31
|
import { getAllowedUserIds, isAuthorizedTelegramSender } from "../harness/telegram-auth";
|
|
32
32
|
// Centralized application logs: the harness ships redacted batches to
|
|
33
|
-
// /api/logs, the server
|
|
34
|
-
|
|
33
|
+
// /api/logs, the server queues them durably and a worker uploads them to
|
|
34
|
+
// Cloudflare R2.
|
|
35
|
+
import {
|
|
36
|
+
LogCredentialStore,
|
|
37
|
+
LogIndexStore,
|
|
38
|
+
LogIngestService,
|
|
39
|
+
LogOutbox,
|
|
40
|
+
LogOutboxWorker,
|
|
41
|
+
createLogHttpHandler,
|
|
42
|
+
serverObjectKey,
|
|
43
|
+
} from "../packages/logging/src/server/index";
|
|
44
|
+
import type { OutboxEntry } from "../packages/logging/src/server/outbox";
|
|
35
45
|
import { loadR2Config, R2Client } from "./r2-storage";
|
|
36
46
|
|
|
37
47
|
// --- Configuration ---
|
|
@@ -78,8 +88,37 @@ const CALLBACK_QUEUE_FILE = join(process.env.HOME || homedir() || "/tmp", ".pi-h
|
|
|
78
88
|
|
|
79
89
|
// --- Log ingest (centralized application logs) ---
|
|
80
90
|
const LOG_INGEST_DIR = join(process.env.HOME || homedir() || "/tmp", ".pi-harness-runtime", "logs", "inbound");
|
|
91
|
+
/** Durable queue of accepted batches (server side outbox). */
|
|
92
|
+
const LOG_OUTBOX_DIR = join(process.env.HOME || homedir() || "/tmp", ".pi-harness-runtime", "logs", "outbox");
|
|
93
|
+
/** Metadata index used by the query endpoint. */
|
|
94
|
+
const LOG_INDEX_DIR = join(process.env.HOME || homedir() || "/tmp", ".pi-harness-runtime", "logs", "index");
|
|
81
95
|
/** Largest accepted ingest body (2 MB). A batch is NDJSON, so this is generous. */
|
|
82
96
|
const LOG_INGEST_MAX_BYTES = 2 * 1024 * 1024;
|
|
97
|
+
/** Largest accepted query body (64 KB is plenty for a filter object). */
|
|
98
|
+
const LOG_QUERY_MAX_BYTES = 64 * 1024;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The ingest pipeline.
|
|
102
|
+
*
|
|
103
|
+
* The credential store decides whether the endpoint exists at all: without a
|
|
104
|
+
* credential the endpoint refuses every write (fail closed) instead of
|
|
105
|
+
* accepting anonymous batches.
|
|
106
|
+
*/
|
|
107
|
+
const logCredentials = LogCredentialStore.load();
|
|
108
|
+
const logOutbox = new LogOutbox({ dir: LOG_OUTBOX_DIR });
|
|
109
|
+
const logIndex = new LogIndexStore({ dir: LOG_INDEX_DIR });
|
|
110
|
+
const logService = new LogIngestService({
|
|
111
|
+
credentials: logCredentials,
|
|
112
|
+
outbox: logOutbox,
|
|
113
|
+
index: logIndex,
|
|
114
|
+
});
|
|
115
|
+
/** The HTTP surface of the log service (ingest, query, diagnostics). */
|
|
116
|
+
const logHttpHandler = createLogHttpHandler({
|
|
117
|
+
service: logService,
|
|
118
|
+
outbox: logOutbox,
|
|
119
|
+
index: logIndex,
|
|
120
|
+
localCopyDir: LOG_INGEST_DIR,
|
|
121
|
+
});
|
|
83
122
|
|
|
84
123
|
/**
|
|
85
124
|
* Persist a callback to disk when no WebSocket client is connected.
|
|
@@ -96,93 +135,40 @@ function queueCallback(payload: CallbackPayload): void {
|
|
|
96
135
|
}
|
|
97
136
|
|
|
98
137
|
/**
|
|
99
|
-
*
|
|
138
|
+
* Read a request body with a hard size limit.
|
|
100
139
|
*
|
|
101
|
-
*
|
|
102
|
-
* (defence in depth) and writes NDJSON next to the other harness logs. The
|
|
103
|
-
* R2 relay is best effort: a failed upload never fails the request, because
|
|
104
|
-
* the batch is already on disk.
|
|
140
|
+
* Returns null when the body was too large (the response is already sent).
|
|
105
141
|
*/
|
|
106
|
-
async function
|
|
107
|
-
const ingestToken = process.env.LOG_INGEST_TOKEN;
|
|
108
|
-
if (ingestToken) {
|
|
109
|
-
const auth = req.headers.authorization;
|
|
110
|
-
if (auth !== `Bearer ${ingestToken}`) {
|
|
111
|
-
res.writeHead(403, { "Content-Type": "application/json" });
|
|
112
|
-
res.end(JSON.stringify({ error: "forbidden" }));
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
142
|
+
async function readBody(req: IncomingMessage, maxBytes: number): Promise<Buffer | null> {
|
|
117
143
|
const chunks: Buffer[] = [];
|
|
118
144
|
let size = 0;
|
|
119
145
|
for await (const chunk of req) {
|
|
120
146
|
size += (chunk as Buffer).length;
|
|
121
|
-
if (size >
|
|
122
|
-
|
|
123
|
-
res.end(JSON.stringify({ error: "payload too large" }));
|
|
124
|
-
return;
|
|
147
|
+
if (size > maxBytes) {
|
|
148
|
+
return null;
|
|
125
149
|
}
|
|
126
150
|
chunks.push(chunk as Buffer);
|
|
127
151
|
}
|
|
128
|
-
|
|
129
|
-
let payload: { batchId?: string; records?: unknown[] };
|
|
130
|
-
try {
|
|
131
|
-
payload = JSON.parse(Buffer.concat(chunks).toString());
|
|
132
|
-
} catch {
|
|
133
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
134
|
-
res.end(JSON.stringify({ error: "invalid json" }));
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const incoming = Array.isArray(payload.records) ? payload.records : [];
|
|
139
|
-
const records = incoming.map((record) => sanitizeLogRecord(record));
|
|
140
|
-
|
|
141
|
-
// Persist locally first - the R2 relay is best effort.
|
|
142
|
-
try {
|
|
143
|
-
if (!existsSync(LOG_INGEST_DIR)) mkdirSync(LOG_INGEST_DIR, { recursive: true });
|
|
144
|
-
const day = new Date().toISOString().slice(0, 10);
|
|
145
|
-
const file = join(LOG_INGEST_DIR, `logs-${day}.ndjson`);
|
|
146
|
-
appendFileSync(file, records.map((record) => JSON.stringify(record)).join("\n") + "\n");
|
|
147
|
-
} catch (err) {
|
|
148
|
-
console.error("[LogIngest] Failed to persist batch:", err);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
let relayed = false;
|
|
152
|
-
try {
|
|
153
|
-
const r2Config = loadR2Config();
|
|
154
|
-
if (r2Config) {
|
|
155
|
-
const client = new R2Client(r2Config);
|
|
156
|
-
const now = new Date();
|
|
157
|
-
const stamp = `${now.getUTCFullYear()}/${String(now.getUTCMonth() + 1).padStart(2, "0")}/${String(now.getUTCDate()).padStart(2, "0")}`;
|
|
158
|
-
await client.upload({
|
|
159
|
-
key: `logs/inbound/${stamp}/${payload.batchId ?? Date.now()}.ndjson`,
|
|
160
|
-
body: records.map((record) => JSON.stringify(record)).join("\n") + "\n",
|
|
161
|
-
contentType: "application/x-ndjson",
|
|
162
|
-
});
|
|
163
|
-
relayed = true;
|
|
164
|
-
}
|
|
165
|
-
} catch (err) {
|
|
166
|
-
console.error("[LogIngest] Failed to relay batch to R2:", err);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
res.writeHead(202, { "Content-Type": "application/json" });
|
|
170
|
-
res.end(JSON.stringify({ accepted: records.length, relayed }));
|
|
152
|
+
return Buffer.concat(chunks);
|
|
171
153
|
}
|
|
172
154
|
|
|
173
|
-
/**
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Upload one queued batch to R2.
|
|
157
|
+
*
|
|
158
|
+
* The worker calls this in the background: an R2 outage grows the queue
|
|
159
|
+
* instead of blocking the ingest endpoint.
|
|
160
|
+
*/
|
|
161
|
+
async function uploadQueuedBatch(entry: OutboxEntry): Promise<void> {
|
|
162
|
+
const r2Config = loadR2Config();
|
|
163
|
+
if (!r2Config) throw new Error("R2 is not configured");
|
|
164
|
+
const client = new R2Client(r2Config);
|
|
165
|
+
const key = serverObjectKey({
|
|
166
|
+
tenantId: entry.batch.tenantId,
|
|
167
|
+
batchId: entry.batch.batchId,
|
|
168
|
+
sentAt: entry.batch.sentAt,
|
|
169
|
+
});
|
|
170
|
+
const body = entry.batch.records.map((record) => JSON.stringify(record)).join("\n") + "\n";
|
|
171
|
+
await client.upload({ key, body, contentType: "application/x-ndjson" });
|
|
186
172
|
}
|
|
187
173
|
|
|
188
174
|
interface JobCommand {
|
|
@@ -649,9 +635,13 @@ async function main() {
|
|
|
649
635
|
acknowledgeCommands();
|
|
650
636
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
651
637
|
res.end(JSON.stringify({ success: true }));
|
|
652
|
-
} else if (
|
|
653
|
-
|
|
654
|
-
|
|
638
|
+
} else if (
|
|
639
|
+
url.pathname === "/api/logs" ||
|
|
640
|
+
url.pathname === "/api/logs/query" ||
|
|
641
|
+
url.pathname === "/api/logs/health"
|
|
642
|
+
) {
|
|
643
|
+
// Centralized application logs: ingest, query and diagnostics.
|
|
644
|
+
await logHttpHandler(req, res);
|
|
655
645
|
} else if (url.pathname === "/health") {
|
|
656
646
|
// Keep the plain-text contract that external monitors check for "OK".
|
|
657
647
|
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
@@ -684,9 +674,24 @@ async function main() {
|
|
|
684
674
|
if (config.line) console.log(` LINE: https://webhook.bunchee.online/line`);
|
|
685
675
|
});
|
|
686
676
|
|
|
677
|
+
// The outbox worker uploads the accepted batches to R2 in the background:
|
|
678
|
+
// an R2 outage grows the queue instead of blocking the ingest endpoint.
|
|
679
|
+
const outboxWorker = new LogOutboxWorker({ outbox: logOutbox, upload: uploadQueuedBatch });
|
|
680
|
+
outboxWorker.start();
|
|
681
|
+
if (!logService.configured) {
|
|
682
|
+
console.error(
|
|
683
|
+
"[LogIngest] No ingest credential configured (LOG_INGEST_TOKEN, keys/log-ingest-token.txt or keys/log-ingest-credentials.json) - /api/logs will refuse every write.",
|
|
684
|
+
);
|
|
685
|
+
} else {
|
|
686
|
+
console.log(
|
|
687
|
+
`[LogIngest] Ingest enabled - ${logOutbox.stats().pending} batch(es) queued, outbox at ${LOG_OUTBOX_DIR}`,
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
687
691
|
// Graceful shutdown
|
|
688
692
|
const shutdown = async () => {
|
|
689
693
|
console.log("\nShutting down...");
|
|
694
|
+
await outboxWorker.stop();
|
|
690
695
|
await relayServer.stop();
|
|
691
696
|
server.close();
|
|
692
697
|
process.exit(0);
|