pi-harness-runtime 1.1.395 → 1.1.397

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.
Files changed (210) hide show
  1. package/AGENTS.md +64 -8
  2. package/package.json +1 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/connection-runtime/package.json +1 -1
  21. package/packages/context-compiler/package.json +1 -1
  22. package/packages/context-discovery/package.json +1 -1
  23. package/packages/context-manager/package.json +1 -1
  24. package/packages/contracts/dist/workspace-env-format.d.ts +24 -0
  25. package/packages/contracts/dist/workspace-env-format.js +41 -1
  26. package/packages/contracts/dist/workspace-env.js +7 -4
  27. package/packages/contracts/package.json +1 -1
  28. package/packages/contracts/src/workspace-env-format.ts +38 -1
  29. package/packages/contracts/src/workspace-env.ts +8 -3
  30. package/packages/contracts/test/workspace-env.test.ts +48 -0
  31. package/packages/cookie-sanitizer/package.json +1 -1
  32. package/packages/cost-optimizer/package.json +1 -1
  33. package/packages/dependency-analyzer/package.json +1 -1
  34. package/packages/django-plugin/package.json +1 -1
  35. package/packages/doc-generator/package.json +1 -1
  36. package/packages/durable-review/package.json +1 -1
  37. package/packages/e2e-tester/package.json +1 -1
  38. package/packages/evaluation-engine/package.json +1 -1
  39. package/packages/evaluation-runner/package.json +1 -1
  40. package/packages/event-bus/package.json +1 -1
  41. package/packages/event-sink/package.json +1 -1
  42. package/packages/event-store/package.json +1 -1
  43. package/packages/experience-replay/package.json +1 -1
  44. package/packages/feedback-collector/package.json +1 -1
  45. package/packages/file-copy-helper/package.json +1 -1
  46. package/packages/framework-detector/package.json +1 -1
  47. package/packages/framework-plugin-sdk/package.json +1 -1
  48. package/packages/frappe-plugin/package.json +1 -1
  49. package/packages/generic-web-plugin/package.json +1 -1
  50. package/packages/health-monitor/package.json +1 -1
  51. package/packages/intent-analyzer/package.json +1 -1
  52. package/packages/jev-judge/package.json +1 -1
  53. package/packages/knowledge-graph/package.json +1 -1
  54. package/packages/knowledge-retrieval/package.json +1 -1
  55. package/packages/laravel-plugin/package.json +1 -1
  56. package/packages/learning-engine/package.json +1 -1
  57. package/packages/logging/README.md +41 -6
  58. package/packages/logging/dist/classification.d.ts +65 -0
  59. package/packages/logging/dist/classification.d.ts.map +1 -0
  60. package/packages/logging/dist/classification.js +155 -0
  61. package/packages/logging/dist/classification.js.map +1 -0
  62. package/packages/logging/dist/config.d.ts +9 -0
  63. package/packages/logging/dist/config.d.ts.map +1 -1
  64. package/packages/logging/dist/config.js +40 -3
  65. package/packages/logging/dist/config.js.map +1 -1
  66. package/packages/logging/dist/index.d.ts +9 -1
  67. package/packages/logging/dist/index.d.ts.map +1 -1
  68. package/packages/logging/dist/index.js +10 -1
  69. package/packages/logging/dist/index.js.map +1 -1
  70. package/packages/logging/dist/ingest/schema.d.ts +60 -0
  71. package/packages/logging/dist/ingest/schema.d.ts.map +1 -0
  72. package/packages/logging/dist/ingest/schema.js +245 -0
  73. package/packages/logging/dist/ingest/schema.js.map +1 -0
  74. package/packages/logging/dist/installation.d.ts +32 -0
  75. package/packages/logging/dist/installation.d.ts.map +1 -0
  76. package/packages/logging/dist/installation.js +64 -0
  77. package/packages/logging/dist/installation.js.map +1 -0
  78. package/packages/logging/dist/sensitive-keys.d.ts +12 -0
  79. package/packages/logging/dist/sensitive-keys.d.ts.map +1 -0
  80. package/packages/logging/dist/sensitive-keys.js +39 -0
  81. package/packages/logging/dist/sensitive-keys.js.map +1 -0
  82. package/packages/logging/dist/server/credentials.d.ts +85 -0
  83. package/packages/logging/dist/server/credentials.d.ts.map +1 -0
  84. package/packages/logging/dist/server/credentials.js +147 -0
  85. package/packages/logging/dist/server/credentials.js.map +1 -0
  86. package/packages/logging/dist/server/http.d.ts +36 -0
  87. package/packages/logging/dist/server/http.d.ts.map +1 -0
  88. package/packages/logging/dist/server/http.js +139 -0
  89. package/packages/logging/dist/server/http.js.map +1 -0
  90. package/packages/logging/dist/server/index-store.d.ts +95 -0
  91. package/packages/logging/dist/server/index-store.d.ts.map +1 -0
  92. package/packages/logging/dist/server/index-store.js +190 -0
  93. package/packages/logging/dist/server/index-store.js.map +1 -0
  94. package/packages/logging/dist/server/index.d.ts +22 -0
  95. package/packages/logging/dist/server/index.d.ts.map +1 -0
  96. package/packages/logging/dist/server/index.js +15 -0
  97. package/packages/logging/dist/server/index.js.map +1 -0
  98. package/packages/logging/dist/server/keys.d.ts +22 -0
  99. package/packages/logging/dist/server/keys.d.ts.map +1 -0
  100. package/packages/logging/dist/server/keys.js +24 -0
  101. package/packages/logging/dist/server/keys.js.map +1 -0
  102. package/packages/logging/dist/server/outbox-worker.d.ts +55 -0
  103. package/packages/logging/dist/server/outbox-worker.d.ts.map +1 -0
  104. package/packages/logging/dist/server/outbox-worker.js +95 -0
  105. package/packages/logging/dist/server/outbox-worker.js.map +1 -0
  106. package/packages/logging/dist/server/outbox.d.ts +112 -0
  107. package/packages/logging/dist/server/outbox.d.ts.map +1 -0
  108. package/packages/logging/dist/server/outbox.js +247 -0
  109. package/packages/logging/dist/server/outbox.js.map +1 -0
  110. package/packages/logging/dist/server/service.d.ts +83 -0
  111. package/packages/logging/dist/server/service.d.ts.map +1 -0
  112. package/packages/logging/dist/server/service.js +144 -0
  113. package/packages/logging/dist/server/service.js.map +1 -0
  114. package/packages/logging/dist/shipper.d.ts +20 -4
  115. package/packages/logging/dist/shipper.d.ts.map +1 -1
  116. package/packages/logging/dist/shipper.js +31 -5
  117. package/packages/logging/dist/shipper.js.map +1 -1
  118. package/packages/logging/dist/singleton.d.ts.map +1 -1
  119. package/packages/logging/dist/singleton.js +3 -0
  120. package/packages/logging/dist/singleton.js.map +1 -1
  121. package/packages/logging/dist/types.d.ts +28 -1
  122. package/packages/logging/dist/types.d.ts.map +1 -1
  123. package/packages/logging/dist/types.js +8 -0
  124. package/packages/logging/dist/types.js.map +1 -1
  125. package/packages/logging/package.json +1 -1
  126. package/packages/logging/src/classification.ts +175 -0
  127. package/packages/logging/src/config.ts +60 -3
  128. package/packages/logging/src/index.ts +45 -1
  129. package/packages/logging/src/ingest/schema.ts +295 -0
  130. package/packages/logging/src/installation.ts +76 -0
  131. package/packages/logging/src/sensitive-keys.ts +40 -0
  132. package/packages/logging/src/server/credentials.ts +198 -0
  133. package/packages/logging/src/server/http.ts +170 -0
  134. package/packages/logging/src/server/index-store.ts +242 -0
  135. package/packages/logging/src/server/index.ts +56 -0
  136. package/packages/logging/src/server/keys.ts +32 -0
  137. package/packages/logging/src/server/outbox-worker.ts +124 -0
  138. package/packages/logging/src/server/outbox.ts +316 -0
  139. package/packages/logging/src/server/service.ts +196 -0
  140. package/packages/logging/src/shipper.ts +54 -8
  141. package/packages/logging/src/singleton.ts +3 -0
  142. package/packages/logging/src/types.ts +29 -1
  143. package/packages/logging/test/classification.test.ts +137 -0
  144. package/packages/logging/test/identity-transport.test.ts +197 -0
  145. package/packages/logging/test/ingest-schema.test.ts +184 -0
  146. package/packages/logging/test/integration.test.ts +9 -4
  147. package/packages/logging/test/log-http.test.ts +246 -0
  148. package/packages/logging/test/server.test.ts +444 -0
  149. package/packages/logging/test/shipper.test.ts +11 -11
  150. package/packages/logging/test/spool.test.ts +4 -1
  151. package/packages/mcp-adapter/package.json +1 -1
  152. package/packages/memory-engine/package.json +1 -1
  153. package/packages/milestone-manager/package.json +1 -1
  154. package/packages/model-registry/package.json +1 -1
  155. package/packages/nextjs-plugin/package.json +1 -1
  156. package/packages/notification/package.json +1 -1
  157. package/packages/observability/package.json +1 -1
  158. package/packages/okf-indexer/package.json +1 -1
  159. package/packages/paperclip-integration/package.json +1 -1
  160. package/packages/performance-optimizer/package.json +1 -1
  161. package/packages/permission-mode/package.json +1 -1
  162. package/packages/privilege-broker/package.json +1 -1
  163. package/packages/project-analyzer/package.json +1 -1
  164. package/packages/project-bootstrap/package.json +1 -1
  165. package/packages/projection-engine/package.json +1 -1
  166. package/packages/prompt-compiler/package.json +1 -1
  167. package/packages/prompt-versioning/package.json +1 -1
  168. package/packages/provider-adapter-sdk/package.json +1 -1
  169. package/packages/provider-router/package.json +1 -1
  170. package/packages/provider-selector/package.json +1 -1
  171. package/packages/providers/package.json +1 -1
  172. package/packages/qdrant-skills/package.json +1 -1
  173. package/packages/quota-manager/package.json +1 -1
  174. package/packages/rate-limiter/package.json +1 -1
  175. package/packages/react-vite-plugin/package.json +1 -1
  176. package/packages/relay/package.json +1 -1
  177. package/packages/release-manager/package.json +1 -1
  178. package/packages/requirement-compiler/package.json +1 -1
  179. package/packages/runtime/package.json +1 -1
  180. package/packages/scheduler/package.json +1 -1
  181. package/packages/scheduler-adapter/package.json +1 -1
  182. package/packages/session/package.json +1 -1
  183. package/packages/session-api/package.json +1 -1
  184. package/packages/session-export/package.json +1 -1
  185. package/packages/shared-context/package.json +1 -1
  186. package/packages/skill-mcp-client/package.json +1 -1
  187. package/packages/skill-registry/package.json +1 -1
  188. package/packages/skills/package.json +1 -1
  189. package/packages/sprint-planner/package.json +1 -1
  190. package/packages/subscription-engine/package.json +1 -1
  191. package/packages/task-compiler/package.json +1 -1
  192. package/packages/telegram/package.json +1 -1
  193. package/packages/tencentdb-memory/package.json +1 -1
  194. package/packages/tencentdb-sync/package.json +1 -1
  195. package/packages/test-data-generator/package.json +1 -1
  196. package/packages/test-generator/package.json +1 -1
  197. package/packages/todo-bd-sync/package.json +1 -1
  198. package/packages/token-estimation/package.json +1 -1
  199. package/packages/token-optimizer/package.json +1 -1
  200. package/packages/trajectory/package.json +1 -1
  201. package/packages/tui/package.json +1 -1
  202. package/packages/types/package.json +1 -1
  203. package/packages/voice-api/package.json +1 -1
  204. package/packages/webmcp-tester/package.json +1 -1
  205. package/packages/wiki/package.json +1 -1
  206. package/packages/workflow-events/package.json +1 -1
  207. package/packages/workspace-scanner/package.json +1 -1
  208. package/packages/worktree/package.json +1 -1
  209. package/packages/write-review/package.json +1 -1
  210. package/scripts/unified-webhook-server.ts +86 -81
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Durable server-side outbox.
3
+ *
4
+ * The ingest endpoint must not tell a client "accepted" before the batch
5
+ * survived a crash. The outbox is the durable hand-off point between the
6
+ * HTTP request and the R2 upload:
7
+ *
8
+ * request -> validate -> enqueue (durable) -> 202
9
+ * |
10
+ * v
11
+ * worker -> upload -> done
12
+ *
13
+ * The queue is a directory of JSON files, one per accepted batch:
14
+ *
15
+ * <dir>/pending/<key>.json the batch payload
16
+ * <dir>/pending/<key>.meta.json attempts, next attempt, last error
17
+ * <dir>/done/<key>.json uploaded batches (pruned after a while)
18
+ *
19
+ * A plain file queue is used on purpose: the server runs under Bun, which
20
+ * has no `node:sqlite`, and the queue stays inspectable with `ls`/`jq`.
21
+ *
22
+ * The key is derived from (tenant, installation, batch), so the same batch
23
+ * is enqueued only once - the ingest endpoint is idempotent.
24
+ */
25
+ import { createHash } from "node:crypto";
26
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
27
+ import { join } from "node:path";
28
+ /** Make a value safe to use inside a file name. */
29
+ export function safeSegment(value, maxLength = 48) {
30
+ const cleaned = value
31
+ .replace(/\.\./g, "_")
32
+ .replace(/[^A-Za-z0-9._-]/g, "_")
33
+ .slice(0, maxLength)
34
+ .replace(/^\.+/, "_");
35
+ return cleaned.length > 0 ? cleaned : "unknown";
36
+ }
37
+ /**
38
+ * Build the queue key of a batch.
39
+ *
40
+ * The key is unique per (tenant, installation, batch) and contains no
41
+ * user controlled path segment.
42
+ */
43
+ export function outboxKey(batch) {
44
+ const digest = createHash("sha256")
45
+ .update(`${batch.tenantId}|${batch.installationId}|${batch.batchId}`, "utf8")
46
+ .digest("hex")
47
+ .slice(0, 12);
48
+ return `${safeSegment(batch.tenantId)}--${safeSegment(batch.batchId)}--${digest}`;
49
+ }
50
+ export class LogOutbox {
51
+ dir;
52
+ pendingDir;
53
+ doneDir;
54
+ retentionMs;
55
+ constructor(options) {
56
+ this.dir = options.dir;
57
+ this.pendingDir = join(options.dir, "pending");
58
+ this.doneDir = join(options.dir, "done");
59
+ this.retentionMs = options.retentionMs ?? 7 * 24 * 60 * 60 * 1000;
60
+ try {
61
+ mkdirSync(this.pendingDir, { recursive: true });
62
+ mkdirSync(this.doneDir, { recursive: true });
63
+ }
64
+ catch {
65
+ // The enqueue call reports the failure with a retryable error.
66
+ }
67
+ }
68
+ /** Directory of the queue. */
69
+ get directory() {
70
+ return this.dir;
71
+ }
72
+ /**
73
+ * Store a batch durably.
74
+ *
75
+ * The write is atomic (write to a temp file, then rename), so a crash
76
+ * can never leave a half written batch behind.
77
+ */
78
+ enqueue(batch) {
79
+ const key = outboxKey(batch);
80
+ const payloadPath = join(this.pendingDir, `${key}.json`);
81
+ if (existsSync(payloadPath) || existsSync(join(this.doneDir, `${key}.json`))) {
82
+ return { accepted: true, duplicate: true };
83
+ }
84
+ const tempPath = `${payloadPath}.tmp`;
85
+ try {
86
+ writeFileSync(tempPath, `${JSON.stringify(batch)}\n`, { mode: 0o600 });
87
+ renameSync(tempPath, payloadPath);
88
+ }
89
+ catch (error) {
90
+ try {
91
+ rmSync(tempPath, { force: true });
92
+ }
93
+ catch {
94
+ // Nothing else to do.
95
+ }
96
+ throw error;
97
+ }
98
+ this.writeMeta(key, {
99
+ attempts: 0,
100
+ nextAttemptAt: Date.now(),
101
+ createdAt: Date.now(),
102
+ });
103
+ return { accepted: true, duplicate: false };
104
+ }
105
+ /** The batches waiting for an upload, oldest first. */
106
+ listPending(limit = 100) {
107
+ const entries = [];
108
+ let names;
109
+ try {
110
+ names = readdirSync(this.pendingDir);
111
+ }
112
+ catch {
113
+ return entries;
114
+ }
115
+ for (const name of names) {
116
+ if (!name.endsWith(".json") || name.endsWith(".meta.json"))
117
+ continue;
118
+ const entry = this.readEntry(join(this.pendingDir, name));
119
+ if (entry)
120
+ entries.push(entry);
121
+ }
122
+ entries.sort((a, b) => a.createdAt - b.createdAt);
123
+ return entries.slice(0, limit);
124
+ }
125
+ /** Mark a batch as uploaded and move it out of the pending directory. */
126
+ markUploaded(entry) {
127
+ try {
128
+ renameSync(entry.path, join(this.doneDir, `${entry.key}.json`));
129
+ }
130
+ catch {
131
+ // The file may have been moved already; nothing to do.
132
+ }
133
+ try {
134
+ rmSync(join(this.pendingDir, `${entry.key}.meta.json`), { force: true });
135
+ }
136
+ catch {
137
+ // Ignore.
138
+ }
139
+ }
140
+ /**
141
+ * Record a failed upload and schedule the next attempt.
142
+ */
143
+ markFailed(entry, error, nextAttemptAt) {
144
+ this.writeMeta(entry.key, {
145
+ attempts: entry.attempts + 1,
146
+ nextAttemptAt,
147
+ lastError: error,
148
+ createdAt: entry.createdAt,
149
+ });
150
+ }
151
+ /** Queue depth, oldest pending age and failure information. */
152
+ stats(now = Date.now()) {
153
+ const entries = this.listPending(Number.MAX_SAFE_INTEGER);
154
+ let oldestPendingAgeMs = 0;
155
+ let failures = 0;
156
+ let lastError;
157
+ for (const entry of entries) {
158
+ const age = now - entry.createdAt;
159
+ if (oldestPendingAgeMs === 0 || age > oldestPendingAgeMs)
160
+ oldestPendingAgeMs = age;
161
+ if (entry.attempts > 0)
162
+ failures++;
163
+ if (entry.lastError)
164
+ lastError = entry.lastError;
165
+ }
166
+ return { pending: entries.length, oldestPendingAgeMs, failures, lastError };
167
+ }
168
+ /** Delete uploaded batches that are older than the retention period. */
169
+ prune(now = Date.now()) {
170
+ let removed = 0;
171
+ let names;
172
+ try {
173
+ names = readdirSync(this.doneDir);
174
+ }
175
+ catch {
176
+ return 0;
177
+ }
178
+ for (const name of names) {
179
+ const path = join(this.doneDir, name);
180
+ try {
181
+ const age = now - statSync(path).mtimeMs;
182
+ if (age > this.retentionMs) {
183
+ rmSync(path, { force: true });
184
+ removed++;
185
+ }
186
+ }
187
+ catch {
188
+ // Ignore a file that vanished.
189
+ }
190
+ }
191
+ return removed;
192
+ }
193
+ writeMeta(key, meta) {
194
+ const path = join(this.pendingDir, `${key}.meta.json`);
195
+ const tempPath = `${path}.tmp`;
196
+ try {
197
+ writeFileSync(tempPath, `${JSON.stringify(meta)}\n`, { mode: 0o600 });
198
+ renameSync(tempPath, path);
199
+ }
200
+ catch {
201
+ // The meta file is an optimisation: without it the batch is still
202
+ // retried, only without the backoff hint.
203
+ }
204
+ }
205
+ readEntry(path) {
206
+ const key = path.slice(path.lastIndexOf("/") + 1).replace(/\.json$/, "");
207
+ try {
208
+ const batch = JSON.parse(readFileSync(path, "utf8"));
209
+ let meta = {
210
+ attempts: 0,
211
+ nextAttemptAt: 0,
212
+ createdAt: 0,
213
+ };
214
+ try {
215
+ const metaPath = join(this.pendingDir, `${key}.meta.json`);
216
+ if (existsSync(metaPath)) {
217
+ meta = { ...meta, ...JSON.parse(readFileSync(metaPath, "utf8")) };
218
+ }
219
+ }
220
+ catch {
221
+ // Fall back to the defaults.
222
+ }
223
+ let createdAt = meta.createdAt;
224
+ if (!createdAt) {
225
+ try {
226
+ createdAt = statSync(path).mtimeMs;
227
+ }
228
+ catch {
229
+ createdAt = Date.now();
230
+ }
231
+ }
232
+ return {
233
+ key,
234
+ path,
235
+ batch,
236
+ attempts: meta.attempts ?? 0,
237
+ nextAttemptAt: meta.nextAttemptAt ?? 0,
238
+ lastError: meta.lastError,
239
+ createdAt,
240
+ };
241
+ }
242
+ catch {
243
+ return null;
244
+ }
245
+ }
246
+ }
247
+ //# sourceMappingURL=outbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/server/outbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The log ingest service.
3
+ *
4
+ * The HTTP handler is a thin adapter around this class, so the whole ingest
5
+ * and query policy is testable without a socket:
6
+ *
7
+ * - authentication happens BEFORE the body is parsed,
8
+ * - the tenant comes from the credential, never from the request body,
9
+ * - a batch is only acknowledged after it is durably queued,
10
+ * - a query is always scoped to the tenant of the caller.
11
+ */
12
+ import { type BatchValidationLimits } from "../ingest/schema.js";
13
+ import { type AuthenticatedCredential, type LogCredentialStore } from "./credentials.js";
14
+ import type { LogIndexStore, LogQueryParams, LogQueryResult } from "./index-store.js";
15
+ import type { LogOutbox } from "./outbox.js";
16
+ export interface IngestSuccess {
17
+ accepted: number;
18
+ batchId: string;
19
+ duplicate: boolean;
20
+ tenantId: string;
21
+ queued: true;
22
+ }
23
+ export interface IngestFailure {
24
+ error: string;
25
+ code: string;
26
+ }
27
+ export interface IngestResult {
28
+ status: number;
29
+ body: IngestSuccess | IngestFailure;
30
+ }
31
+ export interface QuerySuccess {
32
+ entries: LogQueryResult["entries"];
33
+ nextCursor?: string;
34
+ hasMore: boolean;
35
+ scannedFiles: number;
36
+ truncated: boolean;
37
+ tenantId: string;
38
+ }
39
+ export interface LogIngestServiceOptions {
40
+ credentials: LogCredentialStore;
41
+ outbox: LogOutbox;
42
+ index: LogIndexStore;
43
+ /** Validation limits for an inbound batch. */
44
+ limits?: Partial<BatchValidationLimits>;
45
+ }
46
+ export declare class LogIngestService {
47
+ private readonly credentials;
48
+ private readonly outbox;
49
+ private readonly index;
50
+ private readonly limits;
51
+ constructor(options: LogIngestServiceOptions);
52
+ /** True when the ingest endpoint has at least one usable credential. */
53
+ get configured(): boolean;
54
+ /**
55
+ * Authenticate a presented bearer token.
56
+ *
57
+ * The HTTP layer uses it to reject a request before the body is read.
58
+ */
59
+ authenticate(token: string | undefined): AuthenticatedCredential | null;
60
+ /**
61
+ * Accept a batch.
62
+ *
63
+ * The status codes matter:
64
+ * 503 - no credential is configured (fail closed) or the queue is
65
+ * unavailable. The client keeps the batch in its spool.
66
+ * 401 - the credential is missing or unknown.
67
+ * 403 - the credential may not write.
68
+ * 400 - the batch does not match the schema.
69
+ * 202 - the batch is durably queued.
70
+ */
71
+ ingest(body: unknown, bearerToken: string | undefined): IngestResult;
72
+ /**
73
+ * Query the metadata index for the tenant of the caller.
74
+ *
75
+ * The `tenantId` of the request is ignored on purpose - only the
76
+ * credential decides whose records are visible.
77
+ */
78
+ query(params: Omit<LogQueryParams, "tenantId">, bearerToken: string | undefined): {
79
+ status: number;
80
+ body: QuerySuccess | IngestFailure;
81
+ };
82
+ }
83
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAiB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAGN,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,aAAa,CAAC;AAE1D,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,aAAa,GAAG,aAAa,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACvC,WAAW,EAAE,kBAAkB,CAAC;IAChC,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC;IACrB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACxC;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IAExD,YAAY,OAAO,EAAE,uBAAuB,EAK3C;IAED,wEAAwE;IACxE,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,uBAAuB,GAAG,IAAI,CAEtE;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,CA6DnE;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG;QACjF,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,YAAY,GAAG,aAAa,CAAC;KACnC,CA0BA;CACD"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * The log ingest service.
3
+ *
4
+ * The HTTP handler is a thin adapter around this class, so the whole ingest
5
+ * and query policy is testable without a socket:
6
+ *
7
+ * - authentication happens BEFORE the body is parsed,
8
+ * - the tenant comes from the credential, never from the request body,
9
+ * - a batch is only acknowledged after it is durably queued,
10
+ * - a query is always scoped to the tenant of the caller.
11
+ */
12
+ import { validateBatch } from "../ingest/schema.js";
13
+ import { LOG_READ_SCOPE, LOG_WRITE_SCOPE, } from "./credentials.js";
14
+ import { serverObjectKey } from "./keys.js";
15
+ export class LogIngestService {
16
+ credentials;
17
+ outbox;
18
+ index;
19
+ limits;
20
+ constructor(options) {
21
+ this.credentials = options.credentials;
22
+ this.outbox = options.outbox;
23
+ this.index = options.index;
24
+ this.limits = options.limits ?? {};
25
+ }
26
+ /** True when the ingest endpoint has at least one usable credential. */
27
+ get configured() {
28
+ return this.credentials.configured;
29
+ }
30
+ /**
31
+ * Authenticate a presented bearer token.
32
+ *
33
+ * The HTTP layer uses it to reject a request before the body is read.
34
+ */
35
+ authenticate(token) {
36
+ return this.credentials.authenticate(token);
37
+ }
38
+ /**
39
+ * Accept a batch.
40
+ *
41
+ * The status codes matter:
42
+ * 503 - no credential is configured (fail closed) or the queue is
43
+ * unavailable. The client keeps the batch in its spool.
44
+ * 401 - the credential is missing or unknown.
45
+ * 403 - the credential may not write.
46
+ * 400 - the batch does not match the schema.
47
+ * 202 - the batch is durably queued.
48
+ */
49
+ ingest(body, bearerToken) {
50
+ if (!this.credentials.configured) {
51
+ return {
52
+ status: 503,
53
+ body: { error: "log ingest is not configured", code: "ingest_unconfigured" },
54
+ };
55
+ }
56
+ const auth = this.credentials.authenticate(bearerToken);
57
+ if (!auth) {
58
+ return { status: 401, body: { error: "unauthorized", code: "unauthorized" } };
59
+ }
60
+ if (!auth.scopes.includes(LOG_WRITE_SCOPE)) {
61
+ return { status: 403, body: { error: "forbidden", code: "forbidden" } };
62
+ }
63
+ const validation = validateBatch(body, { limits: this.limits });
64
+ if (!validation.ok) {
65
+ return {
66
+ status: 400,
67
+ body: { error: validation.error.message, code: validation.error.code },
68
+ };
69
+ }
70
+ const incoming = validation.batch;
71
+ const batch = {
72
+ tenantId: auth.tenantId,
73
+ installationId: incoming.installationId,
74
+ batchId: incoming.batchId,
75
+ app: incoming.app,
76
+ sentAt: incoming.sentAt,
77
+ records: incoming.records,
78
+ };
79
+ if (incoming.appVersion)
80
+ batch.appVersion = incoming.appVersion;
81
+ if (incoming.hostLabel)
82
+ batch.hostLabel = incoming.hostLabel;
83
+ if (incoming.sessionId)
84
+ batch.sessionId = incoming.sessionId;
85
+ try {
86
+ const result = this.outbox.enqueue(batch);
87
+ // The metadata index is best effort: a failing index never loses
88
+ // the batch itself.
89
+ this.index.append(batch, serverObjectKey(batch));
90
+ return {
91
+ status: 202,
92
+ body: {
93
+ accepted: batch.records.length,
94
+ batchId: batch.batchId,
95
+ duplicate: result.duplicate,
96
+ tenantId: batch.tenantId,
97
+ queued: true,
98
+ },
99
+ };
100
+ }
101
+ catch (error) {
102
+ return {
103
+ status: 503,
104
+ body: {
105
+ error: `log queue unavailable: ${error instanceof Error ? error.message : String(error)}`,
106
+ code: "queue_unavailable",
107
+ },
108
+ };
109
+ }
110
+ }
111
+ /**
112
+ * Query the metadata index for the tenant of the caller.
113
+ *
114
+ * The `tenantId` of the request is ignored on purpose - only the
115
+ * credential decides whose records are visible.
116
+ */
117
+ query(params, bearerToken) {
118
+ if (!this.credentials.configured) {
119
+ return {
120
+ status: 503,
121
+ body: { error: "log ingest is not configured", code: "ingest_unconfigured" },
122
+ };
123
+ }
124
+ const auth = this.credentials.authenticate(bearerToken);
125
+ if (!auth) {
126
+ return { status: 401, body: { error: "unauthorized", code: "unauthorized" } };
127
+ }
128
+ if (!auth.scopes.includes(LOG_READ_SCOPE)) {
129
+ return { status: 403, body: { error: "forbidden", code: "forbidden" } };
130
+ }
131
+ const result = this.index.query({ ...params, tenantId: auth.tenantId });
132
+ const body = {
133
+ entries: result.entries,
134
+ hasMore: result.hasMore,
135
+ scannedFiles: result.scannedFiles,
136
+ truncated: result.truncated,
137
+ tenantId: auth.tenantId,
138
+ };
139
+ if (result.nextCursor)
140
+ body.nextCursor = result.nextCursor;
141
+ return { status: 200, body };
142
+ }
143
+ }
144
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"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"}
@@ -10,15 +10,25 @@
10
10
  * next flush, so a temporary outage costs latency, not data.
11
11
  */
12
12
  import { type LogBufferOptions } from "./buffer.js";
13
+ import { type ClassificationPolicy } from "./classification.js";
13
14
  import { type RedactionOptions } from "./redact.js";
14
15
  import { type SamplingPolicy } from "./sampler.js";
15
16
  import { LogSpool } from "./spool.js";
16
- import type { FlushStats, LogInput, LogLevel, LogTransport } from "./types.js";
17
+ import { type FlushStats, type LogInput, type LogLevel, type LogTransport } from "./types.js";
17
18
  export interface LogShipperOptions {
18
19
  /** Application name reported with every batch. */
19
20
  app?: string;
20
- /** Host name reported with every batch. */
21
- host?: string;
21
+ /** Version of the emitting application. */
22
+ appVersion?: string;
23
+ /** Stable id of this installation. */
24
+ installationId?: string;
25
+ /**
26
+ * Optional display label for the machine (never an identity).
27
+ * Defaults to the hostname, which is display metadata only.
28
+ */
29
+ hostLabel?: string;
30
+ /** Opaque session id attached to the batch when the records agree. */
31
+ sessionId?: string;
22
32
  /** Transports in priority order. */
23
33
  transports?: LogTransport[];
24
34
  /** Local spool used when every transport fails. */
@@ -29,6 +39,8 @@ export interface LogShipperOptions {
29
39
  sampling?: Partial<SamplingPolicy>;
30
40
  /** Redaction limits. */
31
41
  redaction?: RedactionOptions;
42
+ /** What may leave the machine. */
43
+ classification?: ClassificationPolicy;
32
44
  /** Interval for the automatic flush timer. Default 15000 ms. */
33
45
  flushIntervalMs?: number;
34
46
  /** Clock injection (tests). */
@@ -38,12 +50,16 @@ export interface LogShipperOptions {
38
50
  }
39
51
  export declare class LogShipper {
40
52
  private readonly app;
41
- private readonly host;
53
+ private readonly appVersion;
54
+ private readonly installationId;
55
+ private readonly hostLabel;
56
+ private readonly sessionId;
42
57
  private readonly transports;
43
58
  private readonly spool;
44
59
  private readonly buffer;
45
60
  private readonly sampler;
46
61
  private readonly redaction;
62
+ private readonly classification;
47
63
  private readonly flushIntervalMs;
48
64
  private readonly now;
49
65
  private readonly createBatchId;
@@ -1 +1 @@
1
- {"version":3,"file":"shipper.d.ts","sourceRoot":"","sources":["../src/shipper.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shipper.d.ts","sourceRoot":"","sources":["../src/shipper.ts"],"names":[],"mappings":"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"}
@@ -11,8 +11,10 @@
11
11
  */
12
12
  import { hostname } from "node:os";
13
13
  import { LogBuffer } from "./buffer.js";
14
+ import { DEFAULT_CLASSIFICATION_POLICY, prepareRemoteRecord, } from "./classification.js";
14
15
  import { redactFields, redactText } from "./redact.js";
15
16
  import { LogSampler } from "./sampler.js";
17
+ import { LOG_SCHEMA_VERSION, } from "./types.js";
16
18
  function defaultBatchId() {
17
19
  const stamp = new Date().toISOString().replace(/[-:.]/g, "").slice(0, 15);
18
20
  return `${stamp}-${Math.random().toString(36).slice(2, 8)}`;
@@ -28,12 +30,16 @@ function toErrorInfo(error) {
28
30
  }
29
31
  export class LogShipper {
30
32
  app;
31
- host;
33
+ appVersion;
34
+ installationId;
35
+ hostLabel;
36
+ sessionId;
32
37
  transports;
33
38
  spool;
34
39
  buffer;
35
40
  sampler;
36
41
  redaction;
42
+ classification;
37
43
  flushIntervalMs;
38
44
  now;
39
45
  createBatchId;
@@ -44,16 +50,21 @@ export class LogShipper {
44
50
  spooled: 0,
45
51
  dropped: 0,
46
52
  sampled: 0,
53
+ localOnly: 0,
47
54
  errors: {},
48
55
  };
49
56
  constructor(options = {}) {
50
57
  this.app = options.app ?? "pi-harness";
51
- this.host = options.host ?? hostname();
58
+ this.appVersion = options.appVersion;
59
+ this.installationId = options.installationId ?? "unknown";
60
+ this.hostLabel = options.hostLabel ?? hostname();
61
+ this.sessionId = options.sessionId;
52
62
  this.transports = options.transports ?? [];
53
63
  this.spool = options.spool;
54
64
  this.buffer = new LogBuffer(options.buffer);
55
65
  this.sampler = new LogSampler(options.sampling);
56
66
  this.redaction = options.redaction ?? {};
67
+ this.classification = options.classification ?? DEFAULT_CLASSIFICATION_POLICY;
57
68
  this.flushIntervalMs = options.flushIntervalMs ?? 15_000;
58
69
  this.now = options.now ?? (() => Date.now());
59
70
  this.createBatchId = options.createBatchId ?? defaultBatchId;
@@ -66,7 +77,13 @@ export class LogShipper {
66
77
  this.stats.sampled++;
67
78
  return;
68
79
  }
69
- this.buffer.push(record);
80
+ // What must stay on the machine never enters the shipping path.
81
+ const remote = prepareRemoteRecord(record, this.classification);
82
+ if (!remote) {
83
+ this.stats.localOnly++;
84
+ return;
85
+ }
86
+ this.buffer.push(remote);
70
87
  if (this.buffer.size >= this.buffer.batchSize) {
71
88
  void this.flush();
72
89
  }
@@ -169,13 +186,22 @@ export class LogShipper {
169
186
  return record;
170
187
  }
171
188
  buildBatch(records) {
172
- return {
189
+ const sessionId = this.sessionId ?? records.find((record) => record.sessionId)?.sessionId;
190
+ const batch = {
191
+ schemaVersion: LOG_SCHEMA_VERSION,
173
192
  batchId: this.createBatchId(),
174
193
  app: this.app,
175
- host: this.host,
194
+ installationId: this.installationId,
176
195
  sentAt: new Date(this.now()).toISOString(),
177
196
  records,
178
197
  };
198
+ if (this.appVersion)
199
+ batch.appVersion = this.appVersion;
200
+ if (this.hostLabel)
201
+ batch.hostLabel = this.hostLabel;
202
+ if (sessionId)
203
+ batch.sessionId = sessionId;
204
+ return batch;
179
205
  }
180
206
  async doFlush() {
181
207
  try {