@hachej/boring-core 0.1.20 → 0.1.22
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/dist/{CoreFront-CDeLdfb0.d.ts → CoreFront-CgAkiEts.d.ts} +1 -0
- package/dist/app/front/index.d.ts +1 -1
- package/dist/app/front/index.js +1 -1
- package/dist/app/server/index.d.ts +6 -3
- package/dist/app/server/index.js +221 -69
- package/dist/{authHook-C5ShLjAS.d.ts → authHook-DUqyxueY.d.ts} +2 -2
- package/dist/{chunk-V5CXMFHP.js → chunk-6D7LEQSL.js} +35 -2
- package/dist/chunk-AQBXNPMD.js +17 -0
- package/dist/{chunk-HSRBZLKT.js → chunk-C3YMOITB.js} +17 -0
- package/dist/{chunk-A5TMALZR.js → chunk-JMCBLJ6W.js} +614 -497
- package/dist/{connection-jW1Xwcne.d.ts → connection-AL8KSENV.d.ts} +1 -1
- package/dist/front/index.d.ts +12 -3
- package/dist/front/index.js +3 -1
- package/dist/{migrate-D49JsATX.d.ts → migrate-B4dwdtGP.d.ts} +1 -1
- package/dist/server/db/index.d.ts +4 -4
- package/dist/server/db/index.js +1 -1
- package/dist/server/index.d.ts +37 -6
- package/dist/server/index.js +2 -2
- package/dist/shared/index.d.ts +15 -1
- package/dist/shared/index.js +7 -1
- package/dist/{index-COZa03RP.d.ts → types-CbMOXLBf.d.ts} +8 -0
- package/drizzle/0010_telemetry_events.sql +12 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { C as CoreFrontAuthPagesOverride } from '../../CoreFront-
|
|
3
|
+
import { C as CoreFrontAuthPagesOverride } from '../../CoreFront-CgAkiEts.js';
|
|
4
4
|
import { WorkspaceAgentSession, WorkspaceAgentFrontProps } from '@hachej/boring-workspace/app/front';
|
|
5
5
|
|
|
6
6
|
interface CoreWorkspaceAgentFrontProps<TSession extends WorkspaceAgentSession = WorkspaceAgentSession> extends Omit<WorkspaceAgentFrontProps<TSession>, 'workspaceId' | 'frontPluginHotReload' | 'hotReloadEnabled'> {
|
package/dist/app/front/index.js
CHANGED
|
@@ -2,9 +2,10 @@ import { RuntimeProvisioningContribution, RegisterAgentRoutesOptions } from '@ha
|
|
|
2
2
|
import { DirPluginEntry, CreateWorkspaceAgentServerOptions } from '@hachej/boring-workspace/app/server';
|
|
3
3
|
import { WorkspaceServerPlugin } from '@hachej/boring-workspace/server';
|
|
4
4
|
import { FastifyInstance } from 'fastify';
|
|
5
|
-
import { C as CoreConfig } from '../../
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { C as CoreConfig } from '../../types-CbMOXLBf.js';
|
|
6
|
+
import { TelemetrySink } from '../../shared/index.js';
|
|
7
|
+
import { B as BetterAuthInstance, L as LoadConfigOptions } from '../../authHook-DUqyxueY.js';
|
|
8
|
+
import { D as Database, U as UserStore, W as WorkspaceStore } from '../../connection-AL8KSENV.js';
|
|
8
9
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
9
10
|
import 'better-auth';
|
|
10
11
|
import 'drizzle-orm/postgres-js';
|
|
@@ -38,6 +39,8 @@ interface CreateCoreWorkspaceAgentServerOptions extends Omit<RegisterAgentRoutes
|
|
|
38
39
|
extraTools?: RegisterAgentRoutesOptions['extraTools'];
|
|
39
40
|
systemPromptAppend?: string;
|
|
40
41
|
serveFrontend?: boolean;
|
|
42
|
+
/** Optional best-effort telemetry sink. Defaults to core's DB-backed env helper. */
|
|
43
|
+
telemetry?: TelemetrySink;
|
|
41
44
|
}
|
|
42
45
|
declare function createCoreWorkspaceAgentServer(options?: CreateCoreWorkspaceAgentServerOptions): Promise<CoreWorkspaceAgentServer>;
|
|
43
46
|
|
package/dist/app/server/index.js
CHANGED
|
@@ -9,13 +9,20 @@ import {
|
|
|
9
9
|
registerRoutes,
|
|
10
10
|
registerSettingsRoutes,
|
|
11
11
|
registerWorkspaceRoutes
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-6D7LEQSL.js";
|
|
13
13
|
import {
|
|
14
14
|
PostgresUserStore,
|
|
15
15
|
PostgresWorkspaceStore,
|
|
16
|
-
createDatabase
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
createDatabase,
|
|
17
|
+
telemetryEvents
|
|
18
|
+
} from "../../chunk-C3YMOITB.js";
|
|
19
|
+
import {
|
|
20
|
+
noopTelemetry,
|
|
21
|
+
safeCapture
|
|
22
|
+
} from "../../chunk-AQBXNPMD.js";
|
|
23
|
+
import {
|
|
24
|
+
ERROR_CODES
|
|
25
|
+
} from "../../chunk-H5KU6R6Y.js";
|
|
19
26
|
import "../../chunk-MLKGABMK.js";
|
|
20
27
|
|
|
21
28
|
// src/app/server/createCoreWorkspaceAgentServer.ts
|
|
@@ -24,13 +31,14 @@ import { createReadStream } from "fs";
|
|
|
24
31
|
import path from "path";
|
|
25
32
|
import {
|
|
26
33
|
compactPiPackages,
|
|
34
|
+
provisionWorkspaceRuntime,
|
|
27
35
|
registerAgentRoutes
|
|
28
36
|
} from "@hachej/boring-agent/server";
|
|
29
37
|
import {
|
|
30
38
|
collectWorkspaceAgentServerPlugins,
|
|
31
39
|
hasDirServerPlugin,
|
|
32
|
-
provisionWorkspaceAgentServer,
|
|
33
40
|
readWorkspacePluginPackagePiSnapshot,
|
|
41
|
+
readWorkspacePluginPackageRuntimePlugins,
|
|
34
42
|
resolveDefaultWorkspacePluginPackagePaths,
|
|
35
43
|
resolveOnePluginEntry
|
|
36
44
|
} from "@hachej/boring-workspace/app/server";
|
|
@@ -39,6 +47,134 @@ import {
|
|
|
39
47
|
createWorkspaceUiTools,
|
|
40
48
|
uiRoutes
|
|
41
49
|
} from "@hachej/boring-workspace/server";
|
|
50
|
+
|
|
51
|
+
// src/server/telemetry/db.ts
|
|
52
|
+
var EVENT_NAME_PATTERN = /^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*){0,8}$/;
|
|
53
|
+
var SAFE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/;
|
|
54
|
+
var SAFE_SLUG_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$/;
|
|
55
|
+
var SAFE_STATUS_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
56
|
+
var SAFE_ERROR_CODE_PATTERN = /^[A-Za-z][A-Za-z0-9_:-]{0,63}$/;
|
|
57
|
+
var SAFE_PACKAGE_NAME_PATTERN = /^(?:@[A-Za-z0-9_.-]+\/)?[A-Za-z0-9_.-]{1,96}$/;
|
|
58
|
+
var SAFE_PACKAGE_VERSION_PATTERN = /^v?\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/;
|
|
59
|
+
var SAFE_BOOLEAN_STRING_PATTERN = /^(?:true|false)$/;
|
|
60
|
+
var SUSPICIOUS_STRING_PATTERN = /(?:secret|token|bearer|password|api[_-]?key|private|\.env|sk[_-](?:live|test)|ghp_|github_pat_|glpat-|xox[baprs]-|AKIA|ASIA|ya29\.|eyJ|phc_|npm_)/i;
|
|
61
|
+
var ALLOWED_PROPERTY_KEYS = /* @__PURE__ */ new Set([
|
|
62
|
+
"workspaceId",
|
|
63
|
+
"sessionId",
|
|
64
|
+
"requestId",
|
|
65
|
+
"runtimeMode",
|
|
66
|
+
"modelProvider",
|
|
67
|
+
"toolName",
|
|
68
|
+
"panelId",
|
|
69
|
+
"commandId",
|
|
70
|
+
"status",
|
|
71
|
+
"durationMs",
|
|
72
|
+
"errorCode",
|
|
73
|
+
"phase",
|
|
74
|
+
"runtime",
|
|
75
|
+
"pluginId",
|
|
76
|
+
"packageId",
|
|
77
|
+
"changed",
|
|
78
|
+
"nodePackageCount",
|
|
79
|
+
"pythonPackageCount",
|
|
80
|
+
"skillCount",
|
|
81
|
+
"templateDirCount",
|
|
82
|
+
"packageName",
|
|
83
|
+
"packageVersion"
|
|
84
|
+
]);
|
|
85
|
+
function createDatabaseTelemetryFromEnv(db, options, env = process.env) {
|
|
86
|
+
if (env.BORING_TELEMETRY_ENABLED !== "true") return noopTelemetry;
|
|
87
|
+
return createDatabaseTelemetry(db, { appId: options.appId, enabled: true });
|
|
88
|
+
}
|
|
89
|
+
function createDatabaseTelemetry(db, options) {
|
|
90
|
+
if (options.enabled === false) return noopTelemetry;
|
|
91
|
+
return {
|
|
92
|
+
capture(event) {
|
|
93
|
+
const eventName = sanitizeTelemetryEventName(event.name);
|
|
94
|
+
if (!eventName) return;
|
|
95
|
+
const row = {
|
|
96
|
+
appId: options.appId,
|
|
97
|
+
eventName,
|
|
98
|
+
distinctId: sanitizeTelemetryDistinctId(event.distinctId),
|
|
99
|
+
properties: sanitizeTelemetryProperties(event.properties)
|
|
100
|
+
};
|
|
101
|
+
try {
|
|
102
|
+
void Promise.resolve(db.insert(telemetryEvents).values(row)).catch(() => {
|
|
103
|
+
});
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function sanitizeTelemetryEventName(value) {
|
|
110
|
+
if (value.length > 128) return void 0;
|
|
111
|
+
if (SUSPICIOUS_STRING_PATTERN.test(value)) return void 0;
|
|
112
|
+
return EVENT_NAME_PATTERN.test(value) ? value : void 0;
|
|
113
|
+
}
|
|
114
|
+
function sanitizeTelemetryDistinctId(value) {
|
|
115
|
+
if (!value) return "anonymous";
|
|
116
|
+
return sanitizeTelemetryString("distinctId", value) ?? "anonymous";
|
|
117
|
+
}
|
|
118
|
+
function sanitizeTelemetryProperties(properties) {
|
|
119
|
+
const sanitized = {};
|
|
120
|
+
if (!properties) return sanitized;
|
|
121
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
122
|
+
if (!ALLOWED_PROPERTY_KEYS.has(key)) continue;
|
|
123
|
+
const sanitizedValue = sanitizeTelemetryProperty(key, value);
|
|
124
|
+
if (sanitizedValue === void 0) continue;
|
|
125
|
+
sanitized[key] = sanitizedValue;
|
|
126
|
+
}
|
|
127
|
+
return sanitized;
|
|
128
|
+
}
|
|
129
|
+
function sanitizeTelemetryProperty(key, value) {
|
|
130
|
+
if (key === "durationMs") {
|
|
131
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
132
|
+
}
|
|
133
|
+
if (key.endsWith("Count")) {
|
|
134
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : void 0;
|
|
135
|
+
}
|
|
136
|
+
if (key === "status" && typeof value === "number") {
|
|
137
|
+
return Number.isInteger(value) && value >= 100 && value <= 599 ? value : void 0;
|
|
138
|
+
}
|
|
139
|
+
if (typeof value !== "string") return void 0;
|
|
140
|
+
return sanitizeTelemetryString(key, value);
|
|
141
|
+
}
|
|
142
|
+
function sanitizeTelemetryString(key, value) {
|
|
143
|
+
if (value.length === 0) return void 0;
|
|
144
|
+
if (SUSPICIOUS_STRING_PATTERN.test(value)) return void 0;
|
|
145
|
+
switch (key) {
|
|
146
|
+
case "workspaceId":
|
|
147
|
+
case "sessionId":
|
|
148
|
+
case "requestId":
|
|
149
|
+
case "distinctId":
|
|
150
|
+
return SAFE_ID_PATTERN.test(value) ? value : void 0;
|
|
151
|
+
case "runtimeMode":
|
|
152
|
+
case "modelProvider":
|
|
153
|
+
case "toolName":
|
|
154
|
+
case "panelId":
|
|
155
|
+
case "commandId":
|
|
156
|
+
case "phase":
|
|
157
|
+
case "runtime":
|
|
158
|
+
return SAFE_SLUG_PATTERN.test(value) ? value : void 0;
|
|
159
|
+
case "pluginId":
|
|
160
|
+
case "packageId":
|
|
161
|
+
return SAFE_ID_PATTERN.test(value) ? value : void 0;
|
|
162
|
+
case "changed":
|
|
163
|
+
return SAFE_BOOLEAN_STRING_PATTERN.test(value) ? value : void 0;
|
|
164
|
+
case "status":
|
|
165
|
+
return SAFE_STATUS_PATTERN.test(value) ? value : void 0;
|
|
166
|
+
case "errorCode":
|
|
167
|
+
return SAFE_ERROR_CODE_PATTERN.test(value) ? value : void 0;
|
|
168
|
+
case "packageName":
|
|
169
|
+
return SAFE_PACKAGE_NAME_PATTERN.test(value) ? value : void 0;
|
|
170
|
+
case "packageVersion":
|
|
171
|
+
return SAFE_PACKAGE_VERSION_PATTERN.test(value) ? value : void 0;
|
|
172
|
+
default:
|
|
173
|
+
return void 0;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/app/server/createCoreWorkspaceAgentServer.ts
|
|
42
178
|
var MIME_TYPES = {
|
|
43
179
|
".css": "text/css; charset=utf-8",
|
|
44
180
|
".html": "text/html; charset=utf-8",
|
|
@@ -62,11 +198,13 @@ var FRONTEND_AUTH_PAGES = /* @__PURE__ */ new Set([
|
|
|
62
198
|
"/auth/signin",
|
|
63
199
|
"/auth/signup",
|
|
64
200
|
"/auth/forgot-password",
|
|
65
|
-
"/auth/reset-password"
|
|
66
|
-
"/auth/callback/github"
|
|
201
|
+
"/auth/reset-password"
|
|
67
202
|
]);
|
|
68
203
|
var FRONTEND_AUTH_PAGES_SPA_ONLY = /* @__PURE__ */ new Set([
|
|
69
|
-
"/auth/verify-email"
|
|
204
|
+
"/auth/verify-email",
|
|
205
|
+
"/auth/error",
|
|
206
|
+
"/auth/callback/github",
|
|
207
|
+
"/auth/callback/google"
|
|
70
208
|
]);
|
|
71
209
|
function dedupeStrings(values) {
|
|
72
210
|
return Array.from(new Set(values));
|
|
@@ -239,10 +377,29 @@ function shouldServeFrontend(pathname) {
|
|
|
239
377
|
if (pathname.startsWith("/auth/")) return false;
|
|
240
378
|
return true;
|
|
241
379
|
}
|
|
242
|
-
async function
|
|
380
|
+
async function serveFrontendShell(request, reply, indexPath, telemetry) {
|
|
381
|
+
if (!await pathExists(indexPath)) {
|
|
382
|
+
reply.status(503);
|
|
383
|
+
return {
|
|
384
|
+
error: "frontend_not_built",
|
|
385
|
+
message: "Build the frontend before starting in production mode."
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
const html = await readFile(indexPath, "utf-8");
|
|
389
|
+
captureAppOpened(telemetry, request.id);
|
|
390
|
+
reply.type("text/html; charset=utf-8");
|
|
391
|
+
return reply.send(injectCspNonceIntoHtml(html, request.cspNonce));
|
|
392
|
+
}
|
|
393
|
+
async function registerAuthProxy(app, options) {
|
|
243
394
|
app.all("/auth/*", async (request, reply) => {
|
|
244
395
|
const accept = String(request.headers?.accept ?? "");
|
|
245
|
-
|
|
396
|
+
const pathname = request.url.split("?")[0] ?? "/";
|
|
397
|
+
const isSpaOnlyAuthPage = FRONTEND_AUTH_PAGES_SPA_ONLY.has(pathname);
|
|
398
|
+
const isExplicitShellAuthPage = FRONTEND_AUTH_PAGES.has(pathname);
|
|
399
|
+
if (request.method === "GET" && accept.includes("text/html") && (isExplicitShellAuthPage || isSpaOnlyAuthPage && pathname !== "/auth/callback/github" && pathname !== "/auth/callback/google")) {
|
|
400
|
+
if (options?.serveSpaShell) {
|
|
401
|
+
return options.serveSpaShell(request, reply);
|
|
402
|
+
}
|
|
246
403
|
return reply.callNotFound();
|
|
247
404
|
}
|
|
248
405
|
const body = encodeAuthRequestBody(request);
|
|
@@ -269,39 +426,36 @@ async function registerAuthProxy(app) {
|
|
|
269
426
|
return reply.send(responseBody);
|
|
270
427
|
});
|
|
271
428
|
}
|
|
272
|
-
|
|
429
|
+
function captureAppOpened(telemetry, requestId) {
|
|
430
|
+
safeCapture(telemetry, {
|
|
431
|
+
name: "app.opened",
|
|
432
|
+
properties: { requestId }
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
function registerTelemetryHooks(app, telemetry) {
|
|
436
|
+
app.addHook("onResponse", async (request, reply) => {
|
|
437
|
+
if (reply.statusCode < 500) return;
|
|
438
|
+
safeCapture(telemetry, {
|
|
439
|
+
name: "server.request.failed",
|
|
440
|
+
properties: {
|
|
441
|
+
requestId: request.id,
|
|
442
|
+
status: reply.statusCode,
|
|
443
|
+
errorCode: ERROR_CODES.INTERNAL_ERROR
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
async function registerFrontendAuthPages(app, appRoot, telemetry) {
|
|
273
449
|
const frontDistDir = path.resolve(appRoot, "dist/front");
|
|
274
450
|
const indexPath = path.resolve(frontDistDir, "index.html");
|
|
275
451
|
for (const pagePath of FRONTEND_AUTH_PAGES) {
|
|
276
|
-
app.get(pagePath, async (request, reply) =>
|
|
277
|
-
if (!await pathExists(indexPath)) {
|
|
278
|
-
reply.status(503);
|
|
279
|
-
return {
|
|
280
|
-
error: "frontend_not_built",
|
|
281
|
-
message: "Build the frontend before starting in production mode."
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
const html = await readFile(indexPath, "utf-8");
|
|
285
|
-
reply.type("text/html; charset=utf-8");
|
|
286
|
-
return reply.send(injectCspNonceIntoHtml(html, request.cspNonce));
|
|
287
|
-
});
|
|
452
|
+
app.get(pagePath, async (request, reply) => serveFrontendShell(request, reply, indexPath, telemetry));
|
|
288
453
|
}
|
|
289
454
|
}
|
|
290
|
-
async function registerFrontendFallback(app, appRoot) {
|
|
455
|
+
async function registerFrontendFallback(app, appRoot, telemetry) {
|
|
291
456
|
const frontDistDir = path.resolve(appRoot, "dist/front");
|
|
292
457
|
const indexPath = path.resolve(frontDistDir, "index.html");
|
|
293
|
-
app.get("/", async (request, reply) =>
|
|
294
|
-
if (!await pathExists(indexPath)) {
|
|
295
|
-
reply.status(503);
|
|
296
|
-
return {
|
|
297
|
-
error: "frontend_not_built",
|
|
298
|
-
message: "Build the frontend before starting in production mode."
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
const html = await readFile(indexPath, "utf-8");
|
|
302
|
-
reply.type("text/html; charset=utf-8");
|
|
303
|
-
return reply.send(injectCspNonceIntoHtml(html, request.cspNonce));
|
|
304
|
-
});
|
|
458
|
+
app.get("/", async (request, reply) => serveFrontendShell(request, reply, indexPath, telemetry));
|
|
305
459
|
app.get("/*", async (request, reply) => {
|
|
306
460
|
const pathname = request.url.split("?")[0] ?? "/";
|
|
307
461
|
if (!shouldServeFrontend(pathname)) return reply.callNotFound();
|
|
@@ -315,16 +469,7 @@ async function registerFrontendFallback(app, appRoot) {
|
|
|
315
469
|
reply.type(contentType(candidate));
|
|
316
470
|
return reply.send(createReadStream(candidate));
|
|
317
471
|
}
|
|
318
|
-
|
|
319
|
-
reply.status(503);
|
|
320
|
-
return {
|
|
321
|
-
error: "frontend_not_built",
|
|
322
|
-
message: "Build the frontend before starting in production mode."
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
const html = await readFile(indexPath, "utf-8");
|
|
326
|
-
reply.type("text/html; charset=utf-8");
|
|
327
|
-
return reply.send(injectCspNonceIntoHtml(html, request.cspNonce));
|
|
472
|
+
return serveFrontendShell(request, reply, indexPath, telemetry);
|
|
328
473
|
});
|
|
329
474
|
}
|
|
330
475
|
async function createCoreRuntime(config) {
|
|
@@ -387,17 +532,24 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
387
532
|
const appRoot = options.appRoot;
|
|
388
533
|
const serveFrontend = options.serveFrontend ?? (process.env.NODE_ENV !== "development" && Boolean(appRoot));
|
|
389
534
|
const workspaceRoot = options.workspaceRoot ?? process.cwd();
|
|
535
|
+
const telemetrySource = options.telemetry ? "custom" : process.env.BORING_TELEMETRY_ENABLED === "true" ? "db-env" : "noop-env";
|
|
536
|
+
const telemetry = options.telemetry ?? createDatabaseTelemetryFromEnv(db, { appId: config.appId }, process.env);
|
|
537
|
+
app.log.debug({ telemetry: { source: telemetrySource } }, "resolved telemetry sink");
|
|
538
|
+
registerTelemetryHooks(app, telemetry);
|
|
390
539
|
await registerCoreRoutes({ app, sql, db, userStore, workspaceStore });
|
|
391
540
|
if (serveFrontend && appRoot) {
|
|
392
|
-
await registerFrontendAuthPages(app, appRoot);
|
|
541
|
+
await registerFrontendAuthPages(app, appRoot, telemetry);
|
|
393
542
|
}
|
|
394
|
-
await registerAuthProxy(app
|
|
543
|
+
await registerAuthProxy(app, serveFrontend && appRoot ? {
|
|
544
|
+
serveSpaShell: (request, reply) => serveFrontendShell(request, reply, path.resolve(appRoot, "dist/front/index.html"), telemetry)
|
|
545
|
+
} : void 0);
|
|
395
546
|
const defaultPluginPackagePaths = resolveDefaultWorkspacePluginPackagePaths({
|
|
396
547
|
workspaceRoot,
|
|
397
548
|
appPackageJsonPath: options.appPackageJsonPath,
|
|
398
549
|
defaultPluginPackages: options.defaultPluginPackages
|
|
399
550
|
});
|
|
400
551
|
const defaultPackagePiSnapshot = readWorkspacePluginPackagePiSnapshot(defaultPluginPackagePaths);
|
|
552
|
+
const defaultPackageRuntimePlugins = readWorkspacePluginPackageRuntimePlugins(defaultPluginPackagePaths);
|
|
401
553
|
const { systemPromptAppend: defaultPackageSystemPromptAppend, ...defaultPackagePiOptions } = defaultPackagePiSnapshot;
|
|
402
554
|
const staticSystemPromptAppend = [options.systemPromptAppend, defaultPackageSystemPromptAppend].filter(Boolean).join("\n\n") || void 0;
|
|
403
555
|
const defaultPluginDirEntries = defaultPluginPackagePaths.map((dir) => ({ dir, hotReload: false })).filter((entry) => hasDirServerPlugin(entry));
|
|
@@ -432,28 +584,9 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
432
584
|
plugins: resolvedPlugins,
|
|
433
585
|
excludeDefaults: options.excludeDefaults
|
|
434
586
|
});
|
|
435
|
-
const provisionedWorkspaceRoots = /* @__PURE__ */ new Map();
|
|
436
|
-
const ensureWorkspaceProvisioned = (root) => {
|
|
437
|
-
if (pluginCollection.provisioningContributions.length === 0) return Promise.resolve();
|
|
438
|
-
const resolvedRoot = path.resolve(root);
|
|
439
|
-
const existing = provisionedWorkspaceRoots.get(resolvedRoot);
|
|
440
|
-
if (existing) return existing;
|
|
441
|
-
const pending = provisionWorkspaceAgentServer({
|
|
442
|
-
workspaceRoot: resolvedRoot,
|
|
443
|
-
provisioningContributions: pluginCollection.provisioningContributions,
|
|
444
|
-
force: options.forceProvisioning
|
|
445
|
-
}).catch((error) => {
|
|
446
|
-
provisionedWorkspaceRoots.delete(resolvedRoot);
|
|
447
|
-
throw error;
|
|
448
|
-
});
|
|
449
|
-
provisionedWorkspaceRoots.set(resolvedRoot, pending);
|
|
450
|
-
return pending;
|
|
451
|
-
};
|
|
452
|
-
await ensureWorkspaceProvisioned(workspaceRoot);
|
|
453
587
|
const resolveWorkspaceId = async (request) => options.getWorkspaceId ? await options.getWorkspaceId(request) : await resolveAuthorizedWorkspaceId(request, workspaceStore);
|
|
454
588
|
const resolveRoot = async (workspaceId, request) => {
|
|
455
589
|
const root = options.getWorkspaceRoot ? await options.getWorkspaceRoot(workspaceId, request) : await resolveWorkspaceRoot(workspaceRoot, workspaceId);
|
|
456
|
-
await ensureWorkspaceProvisioned(root);
|
|
457
590
|
return root;
|
|
458
591
|
};
|
|
459
592
|
const piOptionsByRoot = /* @__PURE__ */ new Map();
|
|
@@ -508,7 +641,26 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
508
641
|
sandboxHandleStore: options.sandboxHandleStore ?? new WorkspaceRuntimeSandboxHandleStore(workspaceStore),
|
|
509
642
|
getWorkspaceId: resolveWorkspaceId,
|
|
510
643
|
getWorkspaceRoot: resolveRoot,
|
|
511
|
-
|
|
644
|
+
provisionRuntime: async ({ provisioningAdapter, runtimeLayout, workspaceId, request, runtimeMode }) => {
|
|
645
|
+
if (!provisioningAdapter) return void 0;
|
|
646
|
+
return await provisionWorkspaceRuntime({
|
|
647
|
+
plugins: [
|
|
648
|
+
...pluginCollection.runtimePlugins,
|
|
649
|
+
...defaultPackageRuntimePlugins
|
|
650
|
+
],
|
|
651
|
+
adapter: provisioningAdapter,
|
|
652
|
+
runtimeLayout,
|
|
653
|
+
telemetry,
|
|
654
|
+
telemetryContext: {
|
|
655
|
+
workspaceId,
|
|
656
|
+
requestId: request?.id,
|
|
657
|
+
runtimeMode
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
},
|
|
661
|
+
provisionWorkspace: options.provisionWorkspace,
|
|
662
|
+
registerHealthRoute: options.registerHealthRoute ?? false,
|
|
663
|
+
telemetry
|
|
512
664
|
});
|
|
513
665
|
await app.register(uiRoutes, {
|
|
514
666
|
getBridge: async (request) => getUiBridge(await resolveWorkspaceId(request)),
|
|
@@ -518,7 +670,7 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
518
670
|
await app.register(routes);
|
|
519
671
|
}
|
|
520
672
|
if (serveFrontend && appRoot) {
|
|
521
|
-
await registerFrontendFallback(app, appRoot);
|
|
673
|
+
await registerFrontendFallback(app, appRoot, telemetry);
|
|
522
674
|
}
|
|
523
675
|
return app;
|
|
524
676
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { C as CoreConfig, R as RuntimeConfig } from './
|
|
1
|
+
import { C as CoreConfig, R as RuntimeConfig } from './types-CbMOXLBf.js';
|
|
2
2
|
import { FastifyPluginAsync } from 'fastify';
|
|
3
3
|
import { Auth } from 'better-auth';
|
|
4
|
-
import { W as WorkspaceStore, D as Database } from './connection-
|
|
4
|
+
import { W as WorkspaceStore, D as Database } from './connection-AL8KSENV.js';
|
|
5
5
|
|
|
6
6
|
interface LoadConfigOptions {
|
|
7
7
|
tomlPath?: string;
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
workspaceRuntimes,
|
|
9
9
|
workspaceSettings,
|
|
10
10
|
workspaces
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-C3YMOITB.js";
|
|
12
12
|
import {
|
|
13
13
|
ConfigValidationError,
|
|
14
14
|
ERROR_CODES,
|
|
@@ -68,6 +68,10 @@ var coreConfigSchema = z.object({
|
|
|
68
68
|
clientId: z.string().min(1),
|
|
69
69
|
clientSecret: z.string().min(1)
|
|
70
70
|
}).optional(),
|
|
71
|
+
google: z.object({
|
|
72
|
+
clientId: z.string().min(1),
|
|
73
|
+
clientSecret: z.string().min(1)
|
|
74
|
+
}).optional(),
|
|
71
75
|
mail: z.object({
|
|
72
76
|
from: z.string().min(1),
|
|
73
77
|
transportUrl: mailTransportUrlSchema
|
|
@@ -77,6 +81,7 @@ var coreConfigSchema = z.object({
|
|
|
77
81
|
}),
|
|
78
82
|
features: z.object({
|
|
79
83
|
githubOauth: z.boolean(),
|
|
84
|
+
googleOauth: z.boolean(),
|
|
80
85
|
invitesEnabled: z.boolean(),
|
|
81
86
|
sendWelcomeEmail: z.boolean(),
|
|
82
87
|
inviteTtlDays: z.number().int().min(1).max(30).default(7)
|
|
@@ -162,6 +167,11 @@ async function loadConfig(options) {
|
|
|
162
167
|
clientId: env.GITHUB_CLIENT_ID,
|
|
163
168
|
clientSecret: env.GITHUB_CLIENT_SECRET
|
|
164
169
|
} : void 0;
|
|
170
|
+
const googleOauth = toml.features?.google_oauth === true;
|
|
171
|
+
const google = env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET ? {
|
|
172
|
+
clientId: env.GOOGLE_CLIENT_ID,
|
|
173
|
+
clientSecret: env.GOOGLE_CLIENT_SECRET
|
|
174
|
+
} : void 0;
|
|
165
175
|
const mailFrom = env.MAIL_FROM;
|
|
166
176
|
const mailTransportUrl = env.MAIL_TRANSPORT_URL;
|
|
167
177
|
const mail = mailFrom && mailTransportUrl ? { from: mailFrom, transportUrl: mailTransportUrl } : void 0;
|
|
@@ -194,6 +204,7 @@ async function loadConfig(options) {
|
|
|
194
204
|
secret: authSecret,
|
|
195
205
|
url: authUrl,
|
|
196
206
|
github,
|
|
207
|
+
google,
|
|
197
208
|
mail,
|
|
198
209
|
sessionTtlSeconds: parseInt(
|
|
199
210
|
env.SESSION_TTL_SECONDS ?? String(THIRTY_DAYS_SECONDS),
|
|
@@ -203,6 +214,7 @@ async function loadConfig(options) {
|
|
|
203
214
|
},
|
|
204
215
|
features: {
|
|
205
216
|
githubOauth: githubOauth && github !== void 0,
|
|
217
|
+
googleOauth: googleOauth && google !== void 0,
|
|
206
218
|
invitesEnabled: toml.features?.invites_enabled ?? true,
|
|
207
219
|
sendWelcomeEmail: env.SEND_WELCOME_EMAIL !== "false",
|
|
208
220
|
...toml.features?.invite_ttl_days != null && { inviteTtlDays: toml.features.invite_ttl_days }
|
|
@@ -222,6 +234,9 @@ function validateConfig(raw) {
|
|
|
222
234
|
}
|
|
223
235
|
return result.data;
|
|
224
236
|
}
|
|
237
|
+
function isGoogleOauthUsable(config) {
|
|
238
|
+
return config.features.googleOauth && config.auth.google !== void 0;
|
|
239
|
+
}
|
|
225
240
|
function buildRuntimeConfigPayload(config) {
|
|
226
241
|
return {
|
|
227
242
|
appId: config.appId,
|
|
@@ -230,6 +245,7 @@ function buildRuntimeConfigPayload(config) {
|
|
|
230
245
|
apiBase: config.auth.url,
|
|
231
246
|
features: {
|
|
232
247
|
githubOauth: config.features.githubOauth,
|
|
248
|
+
googleOauth: isGoogleOauthUsable(config),
|
|
233
249
|
invitesEnabled: config.features.invitesEnabled,
|
|
234
250
|
sendWelcomeEmail: config.features.sendWelcomeEmail
|
|
235
251
|
}
|
|
@@ -362,16 +378,19 @@ function registerCapabilities(app) {
|
|
|
362
378
|
);
|
|
363
379
|
const hasMail = !!app.config.auth.mail;
|
|
364
380
|
app.registerCapabilitiesContributor("core", () => {
|
|
381
|
+
const googleOauth = isGoogleOauthUsable(app.config);
|
|
365
382
|
const core = {
|
|
366
383
|
version: getCoreVersion(),
|
|
367
384
|
features: {
|
|
368
385
|
invitesEnabled: app.config.features.invitesEnabled,
|
|
369
386
|
githubOauth: app.config.features.githubOauth,
|
|
387
|
+
googleOauth,
|
|
370
388
|
emailFlows: hasMail
|
|
371
389
|
},
|
|
372
390
|
auth: {
|
|
373
391
|
emailPassword: true,
|
|
374
392
|
github: false,
|
|
393
|
+
google: googleOauth,
|
|
375
394
|
emailVerification: hasMail,
|
|
376
395
|
passwordReset: hasMail,
|
|
377
396
|
magicLink: hasMail
|
|
@@ -1717,6 +1736,20 @@ function createAuth(config, db, opts) {
|
|
|
1717
1736
|
transport,
|
|
1718
1737
|
logger: opts.logger
|
|
1719
1738
|
}) : void 0;
|
|
1739
|
+
const socialProviders = {
|
|
1740
|
+
...config.auth.github ? {
|
|
1741
|
+
github: {
|
|
1742
|
+
clientId: config.auth.github.clientId,
|
|
1743
|
+
clientSecret: config.auth.github.clientSecret
|
|
1744
|
+
}
|
|
1745
|
+
} : {},
|
|
1746
|
+
...config.features.googleOauth && config.auth.google ? {
|
|
1747
|
+
google: {
|
|
1748
|
+
clientId: config.auth.google.clientId,
|
|
1749
|
+
clientSecret: config.auth.google.clientSecret
|
|
1750
|
+
}
|
|
1751
|
+
} : {}
|
|
1752
|
+
};
|
|
1720
1753
|
return betterAuth({
|
|
1721
1754
|
database: drizzleAdapter(db, { provider: "pg", schema: schema_exports }),
|
|
1722
1755
|
secret: config.auth.secret,
|
|
@@ -1797,7 +1830,7 @@ function createAuth(config, db, opts) {
|
|
|
1797
1830
|
}
|
|
1798
1831
|
},
|
|
1799
1832
|
emailVerification: emailVerificationConfig,
|
|
1800
|
-
socialProviders
|
|
1833
|
+
socialProviders,
|
|
1801
1834
|
plugins
|
|
1802
1835
|
});
|
|
1803
1836
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/shared/telemetry.ts
|
|
2
|
+
var noopTelemetry = {
|
|
3
|
+
capture() {
|
|
4
|
+
}
|
|
5
|
+
};
|
|
6
|
+
function safeCapture(telemetry, event) {
|
|
7
|
+
try {
|
|
8
|
+
void Promise.resolve(telemetry.capture(event)).catch(() => {
|
|
9
|
+
});
|
|
10
|
+
} catch {
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
noopTelemetry,
|
|
16
|
+
safeCapture
|
|
17
|
+
};
|
|
@@ -542,6 +542,7 @@ __export(schema_exports, {
|
|
|
542
542
|
idempotencyKeys: () => idempotencyKeys,
|
|
543
543
|
sessions: () => sessions,
|
|
544
544
|
sessionsRelations: () => sessionsRelations,
|
|
545
|
+
telemetryEvents: () => telemetryEvents,
|
|
545
546
|
userSettings: () => userSettings,
|
|
546
547
|
userSettingsRelations: () => userSettingsRelations,
|
|
547
548
|
users: () => users,
|
|
@@ -866,6 +867,21 @@ var idempotencyKeys = pgTable2(
|
|
|
866
867
|
index2("idempotency_keys_created_at_idx").on(table.createdAt)
|
|
867
868
|
]
|
|
868
869
|
);
|
|
870
|
+
var telemetryEvents = pgTable2(
|
|
871
|
+
"telemetry_events",
|
|
872
|
+
{
|
|
873
|
+
id: uuid2("id").default(sql3`gen_random_uuid()`).primaryKey(),
|
|
874
|
+
appId: text2("app_id").notNull(),
|
|
875
|
+
eventName: text2("event_name").notNull(),
|
|
876
|
+
distinctId: text2("distinct_id").notNull().default("anonymous"),
|
|
877
|
+
properties: jsonb("properties").notNull().default({}),
|
|
878
|
+
createdAt: timestamp2("created_at").defaultNow().notNull()
|
|
879
|
+
},
|
|
880
|
+
(table) => [
|
|
881
|
+
index2("telemetry_events_app_created_at_idx").on(table.appId, table.createdAt),
|
|
882
|
+
index2("telemetry_events_event_name_idx").on(table.eventName)
|
|
883
|
+
]
|
|
884
|
+
);
|
|
869
885
|
|
|
870
886
|
// src/server/db/stores/PostgresWorkspaceStore.ts
|
|
871
887
|
var UI_STATE_KEY_PREFIX = "workspace_ui_state:";
|
|
@@ -1674,6 +1690,7 @@ export {
|
|
|
1674
1690
|
workspaceRuntimes,
|
|
1675
1691
|
workspaceInvites,
|
|
1676
1692
|
idempotencyKeys,
|
|
1693
|
+
telemetryEvents,
|
|
1677
1694
|
schema_exports,
|
|
1678
1695
|
createDatabase,
|
|
1679
1696
|
runMigrations,
|