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,39 @@
1
+ /**
2
+ * Keys that never leave the machine.
3
+ *
4
+ * A key name is a strong hint about its content: "prompt", "output" or
5
+ * "transcript" carry private text, "path" and "url" carry the shape of the
6
+ * user's workspace. The list is shared by the client side classifier and the
7
+ * server side sanitizer, so both sides agree on what is private.
8
+ */
9
+ export const LOCAL_ONLY_FIELD_KEYS = [
10
+ "prompt",
11
+ "response",
12
+ "message",
13
+ "content",
14
+ "transcript",
15
+ "output",
16
+ "stdout",
17
+ "stderr",
18
+ "command",
19
+ "text",
20
+ "body",
21
+ "payload",
22
+ "code",
23
+ "diff",
24
+ "file",
25
+ "path",
26
+ "url",
27
+ "uri",
28
+ "email",
29
+ "user",
30
+ "chat",
31
+ "question",
32
+ "answer",
33
+ ];
34
+ /** True when the key is one that must not be shipped. */
35
+ export function isLocalOnlyFieldKey(key) {
36
+ const lower = key.toLowerCase();
37
+ return LOCAL_ONLY_FIELD_KEYS.some((needle) => lower.includes(needle));
38
+ }
39
+ //# sourceMappingURL=sensitive-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensitive-keys.js","sourceRoot":"","sources":["../src/sensitive-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACpC,QAAQ;IACR,UAAU;IACV,SAAS;IACT,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,MAAM;IACN,MAAM;IACN,SAAS;IACT,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,QAAQ;CACR,CAAC;AAEF,yDAAyD;AACzD,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Scoped ingest credentials.
3
+ *
4
+ * The tenant is derived from the credential, never from the request body:
5
+ * a client cannot claim a tenant it does not own.
6
+ *
7
+ * Two credential shapes are supported:
8
+ *
9
+ * 1. A credential store (`log-ingest-credentials.json`) with one entry per
10
+ * installation. Tokens are stored as SHA-256 hashes, so the file is safe
11
+ * to keep in the key directory and a token can be revoked by removing
12
+ * or revoking its entry.
13
+ * 2. A single shared token (`LOG_INGEST_TOKEN` or the
14
+ * `log-ingest-token.txt` key file) for a single-tenant deployment. It
15
+ * maps to the tenant `LOG_INGEST_TENANT` (default `default`).
16
+ *
17
+ * When neither is configured the store is NOT configured: the ingest
18
+ * endpoint then refuses every write (fail closed) instead of accepting
19
+ * anonymous batches.
20
+ */
21
+ export interface LogCredential {
22
+ /** Stable id of the credential (safe to log). */
23
+ id: string;
24
+ /** The tenant this credential may write for. */
25
+ tenantId: string;
26
+ /** Human readable label, e.g. "laptop". */
27
+ label?: string;
28
+ /** Allowed scopes, e.g. ["logs:write", "logs:read"]. */
29
+ scopes: string[];
30
+ /** SHA-256 hash of the token, prefixed with the algorithm. */
31
+ tokenHash: string;
32
+ createdAt: string;
33
+ /** Set when the credential was revoked. */
34
+ revokedAt?: string | null;
35
+ }
36
+ export interface CredentialFile {
37
+ credentials: LogCredential[];
38
+ }
39
+ export interface AuthenticatedCredential {
40
+ credentialId: string;
41
+ tenantId: string;
42
+ scopes: string[];
43
+ /** True when the credential came from the shared token. */
44
+ shared: boolean;
45
+ }
46
+ export interface LogCredentialStoreOptions {
47
+ /** Path of the credential store file. */
48
+ path?: string;
49
+ /** Environment used for the shared token. */
50
+ env?: Record<string, string | undefined>;
51
+ /** Directory holding the key files. */
52
+ keysDir?: string;
53
+ }
54
+ export declare const DEFAULT_TENANT_ID = "default";
55
+ export declare const LOG_WRITE_SCOPE = "logs:write";
56
+ export declare const LOG_READ_SCOPE = "logs:read";
57
+ /** Hash a token the way the store does. */
58
+ export declare function hashToken(token: string): string;
59
+ export declare class LogCredentialStore {
60
+ private readonly credentials;
61
+ private readonly shared;
62
+ private constructor();
63
+ /**
64
+ * Load the store from the credential file and the shared token.
65
+ *
66
+ * A malformed credential file is ignored (and reported through
67
+ * `loadErrors`), never fatal: the endpoint then fails closed.
68
+ */
69
+ static load(options?: LogCredentialStoreOptions): LogCredentialStore;
70
+ /** True when at least one usable credential exists. */
71
+ get configured(): boolean;
72
+ /** The credentials without their token hashes. */
73
+ list(): Omit<LogCredential, "tokenHash">[];
74
+ /**
75
+ * Authenticate a presented token.
76
+ *
77
+ * Returns null when the token is missing, unknown or revoked.
78
+ */
79
+ authenticate(token: string | undefined): AuthenticatedCredential | null;
80
+ }
81
+ /**
82
+ * Extract the bearer token from an Authorization header.
83
+ */
84
+ export declare function readBearerToken(header: string | undefined): string | undefined;
85
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/server/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,MAAM,WAAW,aAAa;IAC7B,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC9B,WAAW,EAAE,aAAa,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,2DAA2D;IAC3D,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACzC,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAC3C,eAAO,MAAM,eAAe,eAAe,CAAC;AAC5C,eAAO,MAAM,cAAc,cAAc,CAAC;AAE1C,2CAA2C;AAC3C,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAmBD,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkD;IAEzE,OAAO,eAGN;IAED;;;;;OAKG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,GAAE,yBAA8B,GAAG,kBAAkB,CAsCvE;IAED,uDAAuD;IACvD,IAAI,UAAU,IAAI,OAAO,CAKxB;IAED,kDAAkD;IAClD,IAAI,IAAI,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,EAAE,CAEzC;IAED;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,uBAAuB,GAAG,IAAI,CAyBtE;CACD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAI9E"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Scoped ingest credentials.
3
+ *
4
+ * The tenant is derived from the credential, never from the request body:
5
+ * a client cannot claim a tenant it does not own.
6
+ *
7
+ * Two credential shapes are supported:
8
+ *
9
+ * 1. A credential store (`log-ingest-credentials.json`) with one entry per
10
+ * installation. Tokens are stored as SHA-256 hashes, so the file is safe
11
+ * to keep in the key directory and a token can be revoked by removing
12
+ * or revoking its entry.
13
+ * 2. A single shared token (`LOG_INGEST_TOKEN` or the
14
+ * `log-ingest-token.txt` key file) for a single-tenant deployment. It
15
+ * maps to the tenant `LOG_INGEST_TENANT` (default `default`).
16
+ *
17
+ * When neither is configured the store is NOT configured: the ingest
18
+ * endpoint then refuses every write (fail closed) instead of accepting
19
+ * anonymous batches.
20
+ */
21
+ import { createHash, timingSafeEqual } from "node:crypto";
22
+ import { existsSync, readFileSync } from "node:fs";
23
+ import { homedir } from "node:os";
24
+ import { join } from "node:path";
25
+ export const DEFAULT_TENANT_ID = "default";
26
+ export const LOG_WRITE_SCOPE = "logs:write";
27
+ export const LOG_READ_SCOPE = "logs:read";
28
+ /** Hash a token the way the store does. */
29
+ export function hashToken(token) {
30
+ return `sha256:${createHash("sha256").update(token, "utf8").digest("hex")}`;
31
+ }
32
+ function safeEqual(a, b) {
33
+ const left = Buffer.from(a, "utf8");
34
+ const right = Buffer.from(b, "utf8");
35
+ if (left.length !== right.length)
36
+ return false;
37
+ return timingSafeEqual(left, right);
38
+ }
39
+ function readFileTrimmed(path) {
40
+ try {
41
+ if (!existsSync(path))
42
+ return undefined;
43
+ const value = readFileSync(path, "utf8").trim();
44
+ return value.length > 0 ? value : undefined;
45
+ }
46
+ catch {
47
+ return undefined;
48
+ }
49
+ }
50
+ export class LogCredentialStore {
51
+ credentials;
52
+ shared;
53
+ constructor(credentials, shared) {
54
+ this.credentials = credentials;
55
+ this.shared = shared;
56
+ }
57
+ /**
58
+ * Load the store from the credential file and the shared token.
59
+ *
60
+ * A malformed credential file is ignored (and reported through
61
+ * `loadErrors`), never fatal: the endpoint then fails closed.
62
+ */
63
+ static load(options = {}) {
64
+ const env = options.env ?? process.env;
65
+ const keysDir = options.keysDir ?? join(homedir(), ".pi-harness-runtime", "keys");
66
+ const path = options.path ?? join(keysDir, "log-ingest-credentials.json");
67
+ const credentials = [];
68
+ try {
69
+ if (existsSync(path)) {
70
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
71
+ if (Array.isArray(parsed?.credentials)) {
72
+ for (const entry of parsed.credentials) {
73
+ if (entry &&
74
+ typeof entry.id === "string" &&
75
+ typeof entry.tenantId === "string" &&
76
+ typeof entry.tokenHash === "string") {
77
+ credentials.push({
78
+ ...entry,
79
+ scopes: Array.isArray(entry.scopes) ? entry.scopes : [LOG_WRITE_SCOPE],
80
+ createdAt: entry.createdAt ?? new Date(0).toISOString(),
81
+ revokedAt: entry.revokedAt ?? null,
82
+ });
83
+ }
84
+ }
85
+ }
86
+ }
87
+ }
88
+ catch {
89
+ // A broken file must not open the door: the store stays unconfigured.
90
+ }
91
+ const sharedToken = env.LOG_INGEST_TOKEN ?? readFileTrimmed(join(keysDir, "log-ingest-token.txt"));
92
+ const shared = sharedToken
93
+ ? { token: sharedToken, tenantId: env.LOG_INGEST_TENANT ?? DEFAULT_TENANT_ID }
94
+ : undefined;
95
+ return new LogCredentialStore(credentials, shared);
96
+ }
97
+ /** True when at least one usable credential exists. */
98
+ get configured() {
99
+ return (this.shared !== undefined ||
100
+ this.credentials.some((credential) => !credential.revokedAt));
101
+ }
102
+ /** The credentials without their token hashes. */
103
+ list() {
104
+ return this.credentials.map(({ tokenHash: _tokenHash, ...rest }) => rest);
105
+ }
106
+ /**
107
+ * Authenticate a presented token.
108
+ *
109
+ * Returns null when the token is missing, unknown or revoked.
110
+ */
111
+ authenticate(token) {
112
+ if (!token)
113
+ return null;
114
+ if (this.shared && safeEqual(hashToken(token), hashToken(this.shared.token))) {
115
+ return {
116
+ credentialId: "shared",
117
+ tenantId: this.shared.tenantId,
118
+ scopes: [LOG_WRITE_SCOPE, LOG_READ_SCOPE],
119
+ shared: true,
120
+ };
121
+ }
122
+ const presented = hashToken(token);
123
+ for (const credential of this.credentials) {
124
+ if (credential.revokedAt)
125
+ continue;
126
+ if (!safeEqual(presented, credential.tokenHash))
127
+ continue;
128
+ return {
129
+ credentialId: credential.id,
130
+ tenantId: credential.tenantId,
131
+ scopes: credential.scopes,
132
+ shared: false,
133
+ };
134
+ }
135
+ return null;
136
+ }
137
+ }
138
+ /**
139
+ * Extract the bearer token from an Authorization header.
140
+ */
141
+ export function readBearerToken(header) {
142
+ if (!header)
143
+ return undefined;
144
+ const match = /^Bearer\s+(.+)$/i.exec(header.trim());
145
+ return match ? match[1].trim() : undefined;
146
+ }
147
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/server/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * HTTP surface of the log service.
3
+ *
4
+ * The handlers live here (not in the server script) so the whole request
5
+ * path - authentication, size limits, validation, durable acceptance - can
6
+ * be tested over a real socket.
7
+ *
8
+ * Routes:
9
+ * POST /api/logs accept a batch (202 after the durable write)
10
+ * POST /api/logs/query read-only, tenant scoped metadata query
11
+ * GET /api/logs/health queue depth, oldest pending age, failure count
12
+ */
13
+ import type { IncomingMessage, ServerResponse } from "node:http";
14
+ import type { LogIndexStore } from "./index-store.js";
15
+ import type { LogOutbox } from "./outbox.js";
16
+ import type { LogIngestService } from "./service.js";
17
+ export interface LogHttpOptions {
18
+ service: LogIngestService;
19
+ outbox: LogOutbox;
20
+ index: LogIndexStore;
21
+ /** Largest accepted ingest body. Default 2 MB. */
22
+ maxBodyBytes?: number;
23
+ /** Largest accepted query body. Default 64 KB. */
24
+ maxQueryBytes?: number;
25
+ /**
26
+ * Directory for a human readable copy of the accepted batches
27
+ * (`logs-<day>.ndjson`). Optional.
28
+ */
29
+ localCopyDir?: string;
30
+ }
31
+ export type LogHttpHandler = (req: IncomingMessage, res: ServerResponse) => Promise<void>;
32
+ /**
33
+ * Build the HTTP handler for the log routes.
34
+ */
35
+ export declare function createLogHttpHandler(options: LogHttpOptions): LogHttpHandler;
36
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/server/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,kBAAkB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC;IACrB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAoC1F;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CA8F5E"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * HTTP surface of the log service.
3
+ *
4
+ * The handlers live here (not in the server script) so the whole request
5
+ * path - authentication, size limits, validation, durable acceptance - can
6
+ * be tested over a real socket.
7
+ *
8
+ * Routes:
9
+ * POST /api/logs accept a batch (202 after the durable write)
10
+ * POST /api/logs/query read-only, tenant scoped metadata query
11
+ * GET /api/logs/health queue depth, oldest pending age, failure count
12
+ */
13
+ import { appendFileSync, existsSync, mkdirSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { readBearerToken } from "./credentials.js";
16
+ function sendJson(res, status, body) {
17
+ res.writeHead(status, { "Content-Type": "application/json" });
18
+ res.end(JSON.stringify(body));
19
+ }
20
+ async function readBody(req, maxBytes) {
21
+ const chunks = [];
22
+ let size = 0;
23
+ for await (const chunk of req) {
24
+ size += chunk.length;
25
+ if (size > maxBytes)
26
+ return null;
27
+ chunks.push(chunk);
28
+ }
29
+ return Buffer.concat(chunks);
30
+ }
31
+ /**
32
+ * Write a human readable copy of the accepted records next to the other
33
+ * server logs. Best effort: a failing copy never affects the request.
34
+ */
35
+ function writeLocalCopy(dir, payload) {
36
+ if (!dir)
37
+ return;
38
+ const records = payload.records;
39
+ if (!Array.isArray(records) || records.length === 0)
40
+ return;
41
+ try {
42
+ if (!existsSync(dir))
43
+ mkdirSync(dir, { recursive: true });
44
+ const day = new Date().toISOString().slice(0, 10);
45
+ const file = join(dir, `logs-${day}.ndjson`);
46
+ appendFileSync(file, `${records.map((record) => JSON.stringify(record)).join("\n")}\n`);
47
+ }
48
+ catch {
49
+ // The copy is a convenience, not a requirement.
50
+ }
51
+ }
52
+ /**
53
+ * Build the HTTP handler for the log routes.
54
+ */
55
+ export function createLogHttpHandler(options) {
56
+ const maxBodyBytes = options.maxBodyBytes ?? 2 * 1024 * 1024;
57
+ const maxQueryBytes = options.maxQueryBytes ?? 64 * 1024;
58
+ return async (req, res) => {
59
+ const url = new URL(req.url ?? "/", "http://localhost");
60
+ const token = readBearerToken(req.headers.authorization);
61
+ if (url.pathname === "/api/logs" && req.method === "POST") {
62
+ // Fail closed: without a credential the endpoint does not exist.
63
+ if (!options.service.configured) {
64
+ sendJson(res, 503, { error: "log ingest is not configured", code: "ingest_unconfigured" });
65
+ return;
66
+ }
67
+ if (!options.service.authenticate(token)) {
68
+ sendJson(res, 401, { error: "unauthorized", code: "unauthorized" });
69
+ return;
70
+ }
71
+ const raw = await readBody(req, maxBodyBytes);
72
+ if (!raw) {
73
+ sendJson(res, 413, { error: "payload too large", code: "payload_too_large" });
74
+ return;
75
+ }
76
+ let payload;
77
+ try {
78
+ payload = JSON.parse(raw.toString());
79
+ }
80
+ catch {
81
+ sendJson(res, 400, { error: "invalid json", code: "invalid_json" });
82
+ return;
83
+ }
84
+ const result = options.service.ingest(payload, token);
85
+ sendJson(res, result.status, result.body);
86
+ if (result.status === 202)
87
+ writeLocalCopy(options.localCopyDir, payload);
88
+ return;
89
+ }
90
+ if (url.pathname === "/api/logs/query" && req.method === "POST") {
91
+ if (!options.service.configured) {
92
+ sendJson(res, 503, { error: "log ingest is not configured", code: "ingest_unconfigured" });
93
+ return;
94
+ }
95
+ const raw = await readBody(req, maxQueryBytes);
96
+ if (!raw) {
97
+ sendJson(res, 413, { error: "payload too large", code: "payload_too_large" });
98
+ return;
99
+ }
100
+ let params = {};
101
+ if (raw.length > 0) {
102
+ try {
103
+ params = JSON.parse(raw.toString());
104
+ }
105
+ catch {
106
+ sendJson(res, 400, { error: "invalid json", code: "invalid_json" });
107
+ return;
108
+ }
109
+ }
110
+ const result = options.service.query({
111
+ installationId: params.installationId,
112
+ sessionId: params.sessionId,
113
+ since: params.since,
114
+ until: params.until,
115
+ levels: params.levels,
116
+ source: params.source,
117
+ event: params.event,
118
+ limit: params.limit,
119
+ cursor: params.cursor,
120
+ }, token);
121
+ sendJson(res, result.status, result.body);
122
+ return;
123
+ }
124
+ if (url.pathname === "/api/logs/health" && req.method === "GET") {
125
+ if (!options.service.configured) {
126
+ sendJson(res, 503, { configured: false });
127
+ return;
128
+ }
129
+ if (!options.service.authenticate(token)) {
130
+ sendJson(res, 401, { error: "unauthorized", code: "unauthorized" });
131
+ return;
132
+ }
133
+ sendJson(res, 200, { configured: true, outbox: options.outbox.stats() });
134
+ return;
135
+ }
136
+ sendJson(res, 404, { error: "not found" });
137
+ };
138
+ }
139
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/server/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Metadata index for the log query service.
3
+ *
4
+ * R2 is an archive, not a search API, so the server keeps a small metadata
5
+ * index next to it: tenant, machine, session, timestamp, severity,
6
+ * source/event, batch id and the object key of the archived batch.
7
+ *
8
+ * The index deliberately holds NO message text. Prompts, responses and
9
+ * command output never enter it, and an agent asking "which errors happened
10
+ * in this session" gets metadata, not content.
11
+ *
12
+ * Storage is one NDJSON file per tenant and day:
13
+ *
14
+ * <dir>/<tenant>/2026-10-24.ndjson
15
+ *
16
+ * The index is tenant scoped by construction: every query is filtered by the
17
+ * tenant of the authenticated caller, and the tenant of the request body is
18
+ * never used.
19
+ */
20
+ import type { LogLevel } from "../types.js";
21
+ import { type OutboxBatch } from "./outbox.js";
22
+ export interface LogIndexEntry {
23
+ tenantId: string;
24
+ installationId: string;
25
+ sessionId?: string;
26
+ ts: string;
27
+ level: LogLevel;
28
+ source: string;
29
+ event: string;
30
+ batchId: string;
31
+ /** Object key of the archived batch, when it was uploaded. */
32
+ r2Key?: string;
33
+ }
34
+ export interface LogQueryParams {
35
+ /** Tenant of the authenticated caller. Required. */
36
+ tenantId: string;
37
+ installationId?: string;
38
+ sessionId?: string;
39
+ /** ISO timestamp, inclusive lower bound. */
40
+ since?: string;
41
+ /** ISO timestamp, exclusive upper bound. */
42
+ until?: string;
43
+ levels?: LogLevel[];
44
+ source?: string;
45
+ event?: string;
46
+ /** Maximum number of entries. Default 100, hard maximum 500. */
47
+ limit?: number;
48
+ /** Opaque cursor of the previous page. */
49
+ cursor?: string;
50
+ }
51
+ export interface LogQueryResult {
52
+ entries: LogIndexEntry[];
53
+ /** Cursor of the next page, when there is one. */
54
+ nextCursor?: string;
55
+ hasMore: boolean;
56
+ /** Number of day files that were read. */
57
+ scannedFiles: number;
58
+ /** True when the scan hit its day budget. */
59
+ truncated: boolean;
60
+ }
61
+ export interface LogIndexStoreOptions {
62
+ dir: string;
63
+ /** Days a query may scan back. Default 7, hard maximum 31. */
64
+ maxScanDays?: number;
65
+ /** Entries per page by default. */
66
+ defaultLimit?: number;
67
+ /** Hard maximum of entries per page. */
68
+ maxLimit?: number;
69
+ }
70
+ export declare class LogIndexStore {
71
+ private readonly dir;
72
+ private readonly maxScanDays;
73
+ private readonly defaultLimit;
74
+ private readonly maxLimit;
75
+ constructor(options: LogIndexStoreOptions);
76
+ /**
77
+ * Index the metadata of a batch.
78
+ *
79
+ * Returns the number of entries that were written. The write is best
80
+ * effort: a failing index must not lose the batch itself.
81
+ */
82
+ append(batch: OutboxBatch, r2Key?: string): number;
83
+ /**
84
+ * Query the index for one tenant.
85
+ *
86
+ * The result is bounded by the page limit and by the day budget, and the
87
+ * caller is always a tenant: there is no way to query another tenant
88
+ * because the tenant comes from the parameter, which the server fills
89
+ * from the authenticated credential.
90
+ */
91
+ query(params: LogQueryParams): LogQueryResult;
92
+ private matches;
93
+ private dayRange;
94
+ }
95
+ //# sourceMappingURL=index-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-store.d.ts","sourceRoot":"","sources":["../../src/server/index-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC9B,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,SAAS,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAkBD,qBAAa,aAAa;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,YAAY,OAAO,EAAE,oBAAoB,EAKxC;IAED;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CA6BjD;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAsD5C;IAED,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,QAAQ;CAoBhB"}