pi-harness-runtime 1.1.396 → 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.
- package/AGENTS.md +54 -7
- 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/package.json +1 -1
- 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
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingest contract: strict validation and allowlisted redaction.
|
|
3
|
+
*
|
|
4
|
+
* The client and the server share this module, so both sides agree on what a
|
|
5
|
+
* batch is. The server never trusts the body:
|
|
6
|
+
*
|
|
7
|
+
* - the tenant comes from the authenticated credential, never from the JSON,
|
|
8
|
+
* - the batch is validated against a strict, versioned schema,
|
|
9
|
+
* - the records are rebuilt from an allowlist, so an unknown property can
|
|
10
|
+
* never smuggle data into the archive,
|
|
11
|
+
* - every string is redacted again on the server (defence in depth).
|
|
12
|
+
*/
|
|
13
|
+
import { redactText, redactValue } from "../redact.js";
|
|
14
|
+
import { isLocalOnlyFieldKey } from "../sensitive-keys.js";
|
|
15
|
+
import { LOG_SCHEMA_VERSION } from "../types.js";
|
|
16
|
+
export const LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
17
|
+
export const DEFAULT_BATCH_LIMITS = {
|
|
18
|
+
maxRecords: 1000,
|
|
19
|
+
maxStringLength: 8192,
|
|
20
|
+
maxFieldKeys: 64,
|
|
21
|
+
};
|
|
22
|
+
function isPlainObject(value) {
|
|
23
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24
|
+
}
|
|
25
|
+
function isIsoTimestamp(value) {
|
|
26
|
+
return Number.isFinite(Date.parse(value));
|
|
27
|
+
}
|
|
28
|
+
/** Batch ids are opaque but must stay safe to use in an object key. */
|
|
29
|
+
const BATCH_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
30
|
+
const INSTALLATION_ID_PATTERN = /^[A-Za-z0-9._:-]{8,64}$/;
|
|
31
|
+
/**
|
|
32
|
+
* Validate an inbound batch.
|
|
33
|
+
*
|
|
34
|
+
* Unknown properties are NOT an error here - the sanitizer drops them. Only a
|
|
35
|
+
* structurally broken batch is rejected.
|
|
36
|
+
*/
|
|
37
|
+
export function validateBatch(input, options = {}) {
|
|
38
|
+
const limits = { ...DEFAULT_BATCH_LIMITS, ...options.limits };
|
|
39
|
+
const warnings = [];
|
|
40
|
+
if (!isPlainObject(input)) {
|
|
41
|
+
return { ok: false, error: { code: "invalid_batch", message: "batch must be an object" } };
|
|
42
|
+
}
|
|
43
|
+
let schemaVersion = input.schemaVersion;
|
|
44
|
+
if (schemaVersion === undefined) {
|
|
45
|
+
// A client that predates the version marker sends v1.
|
|
46
|
+
schemaVersion = 1;
|
|
47
|
+
warnings.push("batch without a schema version, treated as legacy v1");
|
|
48
|
+
}
|
|
49
|
+
if (typeof schemaVersion !== "number" || !Number.isFinite(schemaVersion)) {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
error: { code: "invalid_schema_version", message: "schemaVersion must be a number" },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (schemaVersion < 1) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
error: { code: "invalid_schema_version", message: "schemaVersion must be >= 1" },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (schemaVersion > LOG_SCHEMA_VERSION) {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
error: {
|
|
65
|
+
code: "unsupported_schema_version",
|
|
66
|
+
message: `schemaVersion ${schemaVersion} is newer than the supported version ${LOG_SCHEMA_VERSION}`,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (schemaVersion < LOG_SCHEMA_VERSION) {
|
|
71
|
+
warnings.push(`legacy schemaVersion ${schemaVersion}`);
|
|
72
|
+
}
|
|
73
|
+
const batchId = input.batchId;
|
|
74
|
+
if (typeof batchId !== "string" || !BATCH_ID_PATTERN.test(batchId)) {
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
error: { code: "invalid_batch_id", message: "batchId is missing or malformed" },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const app = input.app;
|
|
81
|
+
if (typeof app !== "string" || app.length === 0 || app.length > 64) {
|
|
82
|
+
return { ok: false, error: { code: "invalid_app", message: "app is missing or malformed" } };
|
|
83
|
+
}
|
|
84
|
+
const installationId = input.installationId;
|
|
85
|
+
if (installationId !== undefined && typeof installationId !== "string") {
|
|
86
|
+
return {
|
|
87
|
+
ok: false,
|
|
88
|
+
error: { code: "invalid_installation_id", message: "installationId must be a string" },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (schemaVersion >= 2) {
|
|
92
|
+
if (typeof installationId !== "string" || !INSTALLATION_ID_PATTERN.test(installationId)) {
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
error: { code: "invalid_installation_id", message: "installationId is missing or malformed" },
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (!installationId) {
|
|
100
|
+
warnings.push("batch without an installation id");
|
|
101
|
+
}
|
|
102
|
+
const sentAt = input.sentAt;
|
|
103
|
+
if (typeof sentAt !== "string" || !isIsoTimestamp(sentAt)) {
|
|
104
|
+
return { ok: false, error: { code: "invalid_sent_at", message: "sentAt is not a timestamp" } };
|
|
105
|
+
}
|
|
106
|
+
const records = input.records;
|
|
107
|
+
if (!Array.isArray(records)) {
|
|
108
|
+
return { ok: false, error: { code: "invalid_records", message: "records must be an array" } };
|
|
109
|
+
}
|
|
110
|
+
if (records.length === 0) {
|
|
111
|
+
return { ok: false, error: { code: "empty_batch", message: "records must not be empty" } };
|
|
112
|
+
}
|
|
113
|
+
if (records.length > limits.maxRecords) {
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
error: {
|
|
117
|
+
code: "too_many_records",
|
|
118
|
+
message: `records exceed the limit of ${limits.maxRecords}`,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const sanitized = [];
|
|
123
|
+
for (const record of records) {
|
|
124
|
+
const result = sanitizeRecord(record, limits);
|
|
125
|
+
if (!result.ok) {
|
|
126
|
+
return { ok: false, error: result.error };
|
|
127
|
+
}
|
|
128
|
+
sanitized.push(result.record);
|
|
129
|
+
}
|
|
130
|
+
const batch = {
|
|
131
|
+
schemaVersion,
|
|
132
|
+
batchId,
|
|
133
|
+
app,
|
|
134
|
+
installationId: typeof installationId === "string" ? installationId : "legacy",
|
|
135
|
+
sentAt,
|
|
136
|
+
records: sanitized,
|
|
137
|
+
};
|
|
138
|
+
if (typeof input.appVersion === "string")
|
|
139
|
+
batch.appVersion = input.appVersion.slice(0, 32);
|
|
140
|
+
if (typeof input.hostLabel === "string")
|
|
141
|
+
batch.hostLabel = input.hostLabel.slice(0, 128);
|
|
142
|
+
if (typeof input.sessionId === "string")
|
|
143
|
+
batch.sessionId = input.sessionId.slice(0, 128);
|
|
144
|
+
return { ok: true, batch, warnings };
|
|
145
|
+
}
|
|
146
|
+
/** Record keys that are accepted. Everything else is dropped. */
|
|
147
|
+
const ALLOWED_RECORD_KEYS = new Set([
|
|
148
|
+
"ts",
|
|
149
|
+
"level",
|
|
150
|
+
"source",
|
|
151
|
+
"event",
|
|
152
|
+
"message",
|
|
153
|
+
"sessionId",
|
|
154
|
+
"durationMs",
|
|
155
|
+
"fields",
|
|
156
|
+
"error",
|
|
157
|
+
]);
|
|
158
|
+
/**
|
|
159
|
+
* Copy only the allowed properties of a record.
|
|
160
|
+
*
|
|
161
|
+
* This is the allowlist in action: a property the contract does not know is
|
|
162
|
+
* dropped before anything else happens to the record.
|
|
163
|
+
*/
|
|
164
|
+
function pickAllowedProperties(input) {
|
|
165
|
+
const picked = {};
|
|
166
|
+
for (const key of ALLOWED_RECORD_KEYS) {
|
|
167
|
+
if (key in input)
|
|
168
|
+
picked[key] = input[key];
|
|
169
|
+
}
|
|
170
|
+
return picked;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Rebuild one record from the allowlist and redact every string.
|
|
174
|
+
*
|
|
175
|
+
* Unknown properties are dropped, `error.name`, `error.message` and
|
|
176
|
+
* `error.stack` are redacted, and structured fields lose the keys that can
|
|
177
|
+
* carry private content.
|
|
178
|
+
*/
|
|
179
|
+
export function sanitizeRecord(input, limits = {}) {
|
|
180
|
+
const merged = { ...DEFAULT_BATCH_LIMITS, ...limits };
|
|
181
|
+
if (!isPlainObject(input)) {
|
|
182
|
+
return { ok: false, error: { code: "invalid_record", message: "record must be an object" } };
|
|
183
|
+
}
|
|
184
|
+
const allowed = pickAllowedProperties(input);
|
|
185
|
+
const ts = allowed.ts;
|
|
186
|
+
if (typeof ts !== "string" || !isIsoTimestamp(ts)) {
|
|
187
|
+
return { ok: false, error: { code: "invalid_ts", message: "ts is not a timestamp" } };
|
|
188
|
+
}
|
|
189
|
+
const level = allowed.level;
|
|
190
|
+
if (typeof level !== "string" || !LOG_LEVELS.includes(level)) {
|
|
191
|
+
return { ok: false, error: { code: "invalid_level", message: "level is not known" } };
|
|
192
|
+
}
|
|
193
|
+
const source = allowed.source;
|
|
194
|
+
if (typeof source !== "string" || source.length === 0 || source.length > 128) {
|
|
195
|
+
return { ok: false, error: { code: "invalid_source", message: "source is missing or malformed" } };
|
|
196
|
+
}
|
|
197
|
+
const event = allowed.event;
|
|
198
|
+
if (typeof event !== "string" || event.length === 0 || event.length > 128) {
|
|
199
|
+
return { ok: false, error: { code: "invalid_event", message: "event is missing or malformed" } };
|
|
200
|
+
}
|
|
201
|
+
const record = {
|
|
202
|
+
ts,
|
|
203
|
+
level: level,
|
|
204
|
+
source,
|
|
205
|
+
event,
|
|
206
|
+
};
|
|
207
|
+
if (typeof allowed.message === "string") {
|
|
208
|
+
record.message = redactText(allowed.message).slice(0, merged.maxStringLength);
|
|
209
|
+
}
|
|
210
|
+
if (typeof allowed.sessionId === "string") {
|
|
211
|
+
record.sessionId = allowed.sessionId.slice(0, 128);
|
|
212
|
+
}
|
|
213
|
+
if (typeof allowed.durationMs === "number" && Number.isFinite(allowed.durationMs)) {
|
|
214
|
+
record.durationMs = allowed.durationMs;
|
|
215
|
+
}
|
|
216
|
+
if (isPlainObject(allowed.fields)) {
|
|
217
|
+
const fields = {};
|
|
218
|
+
let kept = 0;
|
|
219
|
+
for (const [key, value] of Object.entries(allowed.fields)) {
|
|
220
|
+
if (kept >= merged.maxFieldKeys)
|
|
221
|
+
break;
|
|
222
|
+
if (isLocalOnlyFieldKey(key))
|
|
223
|
+
continue;
|
|
224
|
+
fields[key] = redactValue(value);
|
|
225
|
+
kept++;
|
|
226
|
+
}
|
|
227
|
+
if (Object.keys(fields).length > 0)
|
|
228
|
+
record.fields = fields;
|
|
229
|
+
}
|
|
230
|
+
if (isPlainObject(allowed.error)) {
|
|
231
|
+
const error = { name: "Error", message: "" };
|
|
232
|
+
if (typeof allowed.error.name === "string") {
|
|
233
|
+
error.name = redactText(allowed.error.name).slice(0, 256);
|
|
234
|
+
}
|
|
235
|
+
if (typeof allowed.error.message === "string") {
|
|
236
|
+
error.message = redactText(allowed.error.message).slice(0, merged.maxStringLength);
|
|
237
|
+
}
|
|
238
|
+
if (typeof allowed.error.stack === "string") {
|
|
239
|
+
error.stack = redactText(allowed.error.stack).slice(0, merged.maxStringLength);
|
|
240
|
+
}
|
|
241
|
+
record.error = error;
|
|
242
|
+
}
|
|
243
|
+
return { ok: true, record };
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/ingest/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAmE,MAAM,aAAa,CAAC;AAElH,MAAM,CAAC,MAAM,UAAU,GAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAWlF,MAAM,CAAC,MAAM,oBAAoB,GAA0B;IAC1D,UAAU,EAAE,IAAI;IAChB,eAAe,EAAE,IAAI;IACrB,YAAY,EAAE,EAAE;CAChB,CAAC;AAWF,SAAS,aAAa,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,uEAAuE;AACvE,MAAM,gBAAgB,GAAG,0BAA0B,CAAC;AACpD,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC5B,KAAc,EACd,OAAO,GAAgD,EAAE;IAEzD,MAAM,MAAM,GAAG,EAAE,GAAG,oBAAoB,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAC9D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,yBAAyB,EAAE,EAAE,CAAC;IAC5F,CAAC;IAED,IAAI,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;IACxC,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QACjC,sDAAsD;QACtD,aAAa,GAAG,CAAC,CAAC;QAClB,QAAQ,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1E,OAAO;YACN,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,gCAAgC,EAAE;SACpF,CAAC;IACH,CAAC;IACD,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO;YACN,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,OAAO,EAAE,4BAA4B,EAAE;SAChF,CAAC;IACH,CAAC;IACD,IAAI,aAAa,GAAG,kBAAkB,EAAE,CAAC;QACxC,OAAO;YACN,EAAE,EAAE,KAAK;YACT,KAAK,EAAE;gBACN,IAAI,EAAE,4BAA4B;gBAClC,OAAO,EAAE,iBAAiB,aAAa,wCAAwC,kBAAkB,EAAE;aACnG;SACD,CAAC;IACH,CAAC;IACD,IAAI,aAAa,GAAG,kBAAkB,EAAE,CAAC;QACxC,QAAQ,CAAC,IAAI,CAAC,wBAAwB,aAAa,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACpE,OAAO;YACN,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,iCAAiC,EAAE;SAC/E,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACtB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACpE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,6BAA6B,EAAE,EAAE,CAAC;IAC9F,CAAC;IAED,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;IAC5C,IAAI,cAAc,KAAK,SAAS,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;QACxE,OAAO;YACN,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,iCAAiC,EAAE;SACtF,CAAC;IACH,CAAC;IACD,IAAI,aAAa,IAAI,CAAC,EAAE,CAAC;QACxB,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YACzF,OAAO;gBACN,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,wCAAwC,EAAE;aAC7F,CAAC;QACH,CAAC;IACF,CAAC;SAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,2BAA2B,EAAE,EAAE,CAAC;IAChG,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,CAAC;IAC/F,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,2BAA2B,EAAE,EAAE,CAAC;IAC5F,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QACxC,OAAO;YACN,EAAE,EAAE,KAAK;YACT,KAAK,EAAE;gBACN,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,+BAA+B,MAAM,CAAC,UAAU,EAAE;aAC3D;SACD,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAgB,EAAE,CAAC;IAClC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QAC3C,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,KAAK,GAAa;QACvB,aAAa;QACb,OAAO;QACP,GAAG;QACH,cAAc,EAAE,OAAO,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ;QAC9E,MAAM;QACN,OAAO,EAAE,SAAS;KAClB,CAAC;IAEF,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;QAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3F,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;QAAE,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzF,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;QAAE,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAEzF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACtC,CAAC;AAED,iEAAiE;AACjE,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC;IACnC,IAAI;IACJ,OAAO;IACP,QAAQ;IACR,OAAO;IACP,SAAS;IACT,WAAW;IACX,YAAY;IACZ,QAAQ;IACR,OAAO;CACP,CAAC,CAAC;AAEH;;;;;GAKG;AACH,SAAS,qBAAqB,CAAC,KAA8B;IAC5D,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;QACvC,IAAI,GAAG,IAAI,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAMD;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC7B,KAAc,EACd,MAAM,GAAmC,EAAE;IAE3C,MAAM,MAAM,GAAG,EAAE,GAAG,oBAAoB,EAAE,GAAG,MAAM,EAAE,CAAC;IACtD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,0BAA0B,EAAE,EAAE,CAAC;IAC9F,CAAC;IAED,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAE7C,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;IACtB,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,EAAE,CAAC;QACnD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE,CAAC;IACvF,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAiB,CAAC,EAAE,CAAC;QAC1E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,oBAAoB,EAAE,EAAE,CAAC;IACvF,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC9E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,gCAAgC,EAAE,EAAE,CAAC;IACpG,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC3E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,+BAA+B,EAAE,EAAE,CAAC;IAClG,CAAC;IAED,MAAM,MAAM,GAAc;QACzB,EAAE;QACF,KAAK,EAAE,KAAiB;QACxB,MAAM;QACN,KAAK;KACL,CAAC;IAEF,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzC,MAAM,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3C,MAAM,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACnF,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACxC,CAAC;IAED,IAAI,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnC,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3D,IAAI,IAAI,IAAI,MAAM,CAAC,YAAY;gBAAE,MAAM;YACvC,IAAI,mBAAmB,CAAC,GAAG,CAAC;gBAAE,SAAS;YACvC,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,EAAE,CAAC;QACR,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IAC5D,CAAC;IAED,IAAI,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,KAAK,GAAiB,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QAC3D,IAAI,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC/C,KAAK,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;QACpF,CAAC;QACD,IAAI,OAAO,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC7C,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;QAChF,CAAC;QACD,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable installation identity.
|
|
3
|
+
*
|
|
4
|
+
* One random id per local install. It is created on first use, persisted
|
|
5
|
+
* under `~/.pi-harness-runtime/installation-id.txt` and sent with every
|
|
6
|
+
* batch so the server can tell two machines of the same user apart.
|
|
7
|
+
*
|
|
8
|
+
* The id is deliberately opaque:
|
|
9
|
+
* - it never contains the hostname, the user name or a workspace path,
|
|
10
|
+
* - it is not derived from anything that could identify a person,
|
|
11
|
+
* - it is never used as an identity on the server (the tenant comes from
|
|
12
|
+
* the authenticated credential).
|
|
13
|
+
*/
|
|
14
|
+
export declare const INSTALLATION_ID_FILE = "installation-id.txt";
|
|
15
|
+
/** Default location of the installation id file. */
|
|
16
|
+
export declare function defaultInstallationIdPath(): string;
|
|
17
|
+
/** True when the value looks like a uuid (the only shape we write). */
|
|
18
|
+
export declare function isValidInstallationId(value: string): boolean;
|
|
19
|
+
export interface ResolveInstallationIdOptions {
|
|
20
|
+
/** File the id is read from and written to. */
|
|
21
|
+
path?: string;
|
|
22
|
+
/** Id factory (tests). */
|
|
23
|
+
generate?: () => string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the installation id: read it when it exists, create it otherwise.
|
|
27
|
+
*
|
|
28
|
+
* A failed read or write never throws - the id is best effort metadata and
|
|
29
|
+
* must not break logging.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveInstallationId(options?: ResolveInstallationIdOptions): string;
|
|
32
|
+
//# sourceMappingURL=installation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installation.d.ts","sourceRoot":"","sources":["../src/installation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAE1D,oDAAoD;AACpD,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAID,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,MAAM,WAAW,4BAA4B;IAC5C,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,MAAM,CA6BxF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable installation identity.
|
|
3
|
+
*
|
|
4
|
+
* One random id per local install. It is created on first use, persisted
|
|
5
|
+
* under `~/.pi-harness-runtime/installation-id.txt` and sent with every
|
|
6
|
+
* batch so the server can tell two machines of the same user apart.
|
|
7
|
+
*
|
|
8
|
+
* The id is deliberately opaque:
|
|
9
|
+
* - it never contains the hostname, the user name or a workspace path,
|
|
10
|
+
* - it is not derived from anything that could identify a person,
|
|
11
|
+
* - it is never used as an identity on the server (the tenant comes from
|
|
12
|
+
* the authenticated credential).
|
|
13
|
+
*/
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
15
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
import { dirname, join } from "node:path";
|
|
18
|
+
export const INSTALLATION_ID_FILE = "installation-id.txt";
|
|
19
|
+
/** Default location of the installation id file. */
|
|
20
|
+
export function defaultInstallationIdPath() {
|
|
21
|
+
return join(homedir(), ".pi-harness-runtime", INSTALLATION_ID_FILE);
|
|
22
|
+
}
|
|
23
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24
|
+
/** True when the value looks like a uuid (the only shape we write). */
|
|
25
|
+
export function isValidInstallationId(value) {
|
|
26
|
+
return UUID_PATTERN.test(value.trim());
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the installation id: read it when it exists, create it otherwise.
|
|
30
|
+
*
|
|
31
|
+
* A failed read or write never throws - the id is best effort metadata and
|
|
32
|
+
* must not break logging.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveInstallationId(options = {}) {
|
|
35
|
+
const path = options.path ?? defaultInstallationIdPath();
|
|
36
|
+
const generate = options.generate ?? randomUUID;
|
|
37
|
+
try {
|
|
38
|
+
if (existsSync(path)) {
|
|
39
|
+
const existing = readFileSync(path, "utf8").trim();
|
|
40
|
+
if (isValidInstallationId(existing))
|
|
41
|
+
return existing;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Fall through and create a new id.
|
|
46
|
+
}
|
|
47
|
+
let id;
|
|
48
|
+
try {
|
|
49
|
+
id = generate();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
id = randomUUID();
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
56
|
+
writeFileSync(path, `${id}\n`, { mode: 0o600 });
|
|
57
|
+
chmodSync(path, 0o600);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// The id still works for this process even when it cannot be persisted.
|
|
61
|
+
}
|
|
62
|
+
return id;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=installation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installation.js","sourceRoot":"","sources":["../src/installation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,CAAC,MAAM,oBAAoB,GAAG,qBAAqB,CAAC;AAE1D,oDAAoD;AACpD,MAAM,UAAU,yBAAyB;IACxC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,qBAAqB,EAAE,oBAAoB,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,YAAY,GAAG,iEAAiE,CAAC;AAEvF,uEAAuE;AACvE,MAAM,UAAU,qBAAqB,CAAC,KAAa;IAClD,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACxC,CAAC;AASD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAO,GAAiC,EAAE;IAC/E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,yBAAyB,EAAE,CAAC;IACzD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,UAAU,CAAC;IAEhD,IAAI,CAAC;QACJ,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACnD,IAAI,qBAAqB,CAAC,QAAQ,CAAC;gBAAE,OAAO,QAAQ,CAAC;QACtD,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,oCAAoC;IACrC,CAAC;IAED,IAAI,EAAU,CAAC;IACf,IAAI,CAAC;QACJ,EAAE,GAAG,QAAQ,EAAE,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACR,EAAE,GAAG,UAAU,EAAE,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACJ,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACR,wEAAwE;IACzE,CAAC;IAED,OAAO,EAAE,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
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 declare const LOCAL_ONLY_FIELD_KEYS: string[];
|
|
10
|
+
/** True when the key is one that must not be shipped. */
|
|
11
|
+
export declare function isLocalOnlyFieldKey(key: string): boolean;
|
|
12
|
+
//# sourceMappingURL=sensitive-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitive-keys.d.ts","sourceRoot":"","sources":["../src/sensitive-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,qBAAqB,UAwBjC,CAAC;AAEF,yDAAyD;AACzD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGxD"}
|
|
@@ -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
|