@hachej/boring-core 0.1.54 → 0.1.56
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/app/front/index.js +2 -0
- package/dist/app/server/index.d.ts +23 -1
- package/dist/app/server/index.js +221 -21
- package/package.json +5 -5
package/dist/app/front/index.js
CHANGED
|
@@ -613,6 +613,7 @@ function HomeRedirect({
|
|
|
613
613
|
location.search,
|
|
614
614
|
location.hash
|
|
615
615
|
);
|
|
616
|
+
if (session.isPending) return /* @__PURE__ */ jsx5(Fragment3, { children: resolvedLoadingFallback });
|
|
616
617
|
if (!session.data?.user && chatEntryMode === "chat-first") {
|
|
617
618
|
return /* @__PURE__ */ jsx5(
|
|
618
619
|
ChatFirstPublicShell,
|
|
@@ -701,6 +702,7 @@ function WorkspaceRoute({
|
|
|
701
702
|
[resolvedWorkspaceProps.fullPageBasePath, workspaceId]
|
|
702
703
|
);
|
|
703
704
|
if (!workspaceId) return /* @__PURE__ */ jsx5(Fragment3, { children: resolvedLoadingFallback });
|
|
705
|
+
if (session.isPending) return /* @__PURE__ */ jsx5(Fragment3, { children: resolvedLoadingFallback });
|
|
704
706
|
if (!session.data?.user && chatEntryMode === "chat-first") {
|
|
705
707
|
return /* @__PURE__ */ jsx5(
|
|
706
708
|
ChatFirstPublicShell,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RuntimeProvisioningContribution, RegisterAgentRoutesOptions } from '@hachej/boring-agent/server';
|
|
2
2
|
import { DirPluginEntry, CreateWorkspaceAgentServerOptions } from '@hachej/boring-workspace/app/server';
|
|
3
|
-
import { WorkspaceServerPlugin } from '@hachej/boring-workspace/server';
|
|
3
|
+
import { WorkspaceServerPlugin, WorkspaceBridgeOperationDefinition, WorkspaceBridgeHandler, WorkspaceBridgeRuntimeEnvOptions, WorkspaceBridgeCallRequest, WorkspaceBridgeCallResponse } from '@hachej/boring-workspace/server';
|
|
4
4
|
import { FastifyInstance } from 'fastify';
|
|
5
5
|
import { C as CoreConfig } from '../../types-Bb7I-83I.js';
|
|
6
6
|
import { T as TelemetrySink } from '../../telemetry-DR18MeI0.js';
|
|
@@ -28,6 +28,16 @@ type CoreWorkspaceDirPluginEntry = Omit<DirPluginEntry, 'hotReload'> & {
|
|
|
28
28
|
hotReload?: false;
|
|
29
29
|
};
|
|
30
30
|
type CoreWorkspacePluginEntry = CoreWorkspaceAgentServerPlugin | CoreWorkspaceDirPluginEntry;
|
|
31
|
+
type CoreWorkspaceBridgeExtraTool = NonNullable<RegisterAgentRoutesOptions['extraTools']>[number];
|
|
32
|
+
interface CoreWorkspaceBridgeExtraToolsContext {
|
|
33
|
+
workspaceId: string;
|
|
34
|
+
workspaceRoot: string;
|
|
35
|
+
callAsRuntime<TOutput = unknown>(request: WorkspaceBridgeCallRequest, options?: {
|
|
36
|
+
sessionId?: string;
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
}): Promise<WorkspaceBridgeCallResponse<TOutput>>;
|
|
39
|
+
}
|
|
40
|
+
type CoreWorkspaceBridgePiContext = CoreWorkspaceBridgeExtraToolsContext;
|
|
31
41
|
interface CreateCoreWorkspaceAgentServerOptions extends Omit<RegisterAgentRoutesOptions, 'extraTools'> {
|
|
32
42
|
appRoot?: string;
|
|
33
43
|
config?: CoreConfig;
|
|
@@ -35,6 +45,17 @@ interface CreateCoreWorkspaceAgentServerOptions extends Omit<RegisterAgentRoutes
|
|
|
35
45
|
plugins?: CoreWorkspacePluginEntry[];
|
|
36
46
|
excludeDefaults?: CreateWorkspaceAgentServerOptions['excludeDefaults'];
|
|
37
47
|
defaultPluginPackages?: CreateWorkspaceAgentServerOptions['defaultPluginPackages'];
|
|
48
|
+
workspaceBridge?: {
|
|
49
|
+
handlers?: Array<{
|
|
50
|
+
definition: WorkspaceBridgeOperationDefinition;
|
|
51
|
+
handler: WorkspaceBridgeHandler;
|
|
52
|
+
}>;
|
|
53
|
+
runtimeTokenSecret?: string;
|
|
54
|
+
runtimeRefreshTokenSecret?: string;
|
|
55
|
+
runtimeEnv?: WorkspaceBridgeRuntimeEnvOptions;
|
|
56
|
+
};
|
|
57
|
+
getWorkspaceBridgeExtraTools?: (ctx: CoreWorkspaceBridgeExtraToolsContext) => CoreWorkspaceBridgeExtraTool[] | Promise<CoreWorkspaceBridgeExtraTool[]>;
|
|
58
|
+
getWorkspaceBridgePi?: (ctx: CoreWorkspaceBridgePiContext) => AgentPiOptions | Promise<AgentPiOptions | undefined>;
|
|
38
59
|
/**
|
|
39
60
|
* Enable workspace plugin-authoring tooling/prompt for this app.
|
|
40
61
|
* Defaults to false for full-app/core production composition; set true only
|
|
@@ -50,6 +71,7 @@ interface CreateCoreWorkspaceAgentServerOptions extends Omit<RegisterAgentRoutes
|
|
|
50
71
|
/** Optional best-effort telemetry sink. Defaults to core's DB-backed env helper. */
|
|
51
72
|
telemetry?: TelemetrySink;
|
|
52
73
|
}
|
|
74
|
+
type AgentPiOptions = RegisterAgentRoutesOptions['pi'];
|
|
53
75
|
declare function createCoreWorkspaceAgentServer(options?: CreateCoreWorkspaceAgentServerOptions): Promise<CoreWorkspaceAgentServer>;
|
|
54
76
|
|
|
55
77
|
interface StartCoreWorkspaceAgentDevServerOptions {
|
package/dist/app/server/index.js
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
registerAgentRoutes
|
|
38
38
|
} from "@hachej/boring-agent/server";
|
|
39
39
|
import {
|
|
40
|
+
assertWorkspaceBridgeHandlersTrusted,
|
|
40
41
|
collectWorkspaceAgentServerPlugins,
|
|
41
42
|
hasDirServerPlugin,
|
|
42
43
|
omitPluginAuthoringProvisioning,
|
|
@@ -46,11 +47,156 @@ import {
|
|
|
46
47
|
resolveOnePluginEntry
|
|
47
48
|
} from "@hachej/boring-workspace/app/server";
|
|
48
49
|
import {
|
|
49
|
-
createInMemoryBridge,
|
|
50
50
|
createWorkspaceUiTools,
|
|
51
51
|
uiRoutes
|
|
52
52
|
} from "@hachej/boring-workspace/server";
|
|
53
53
|
|
|
54
|
+
// src/app/server/coreWorkspaceBridge.ts
|
|
55
|
+
import {
|
|
56
|
+
createBrowserBridgeAuthPolicy,
|
|
57
|
+
createInMemoryBridge,
|
|
58
|
+
createWorkspaceBridgeRuntimeCore,
|
|
59
|
+
createWorkspaceBridgeRuntimeEnvContribution,
|
|
60
|
+
InMemoryWorkspaceBridgeIdempotencyStore,
|
|
61
|
+
InMemoryWorkspaceBridgeRuntimeRefreshTokenStore,
|
|
62
|
+
runWithWorkspaceBridgeIdempotency,
|
|
63
|
+
verifyWorkspaceBridgeRuntimeToken,
|
|
64
|
+
workspaceBridgeHttpRoutes
|
|
65
|
+
} from "@hachej/boring-workspace/server";
|
|
66
|
+
var MAX_SESSION_OWNER_CACHE = 5e3;
|
|
67
|
+
function createCoreWorkspaceBridge(options) {
|
|
68
|
+
const { resolveWorkspaceId, workspaceStore, validateWorkspaceId, agentSessionId } = options;
|
|
69
|
+
const bridges = /* @__PURE__ */ new Map();
|
|
70
|
+
const getBridge = (workspaceId) => {
|
|
71
|
+
const safeWorkspaceId = validateWorkspaceId(workspaceId);
|
|
72
|
+
let bridge = bridges.get(safeWorkspaceId);
|
|
73
|
+
if (!bridge) {
|
|
74
|
+
bridge = createInMemoryBridge();
|
|
75
|
+
bridges.set(safeWorkspaceId, bridge);
|
|
76
|
+
}
|
|
77
|
+
return bridge;
|
|
78
|
+
};
|
|
79
|
+
const runtimes = /* @__PURE__ */ new Map();
|
|
80
|
+
const getRuntime = (workspaceId) => {
|
|
81
|
+
const safeWorkspaceId = validateWorkspaceId(workspaceId);
|
|
82
|
+
let runtime = runtimes.get(safeWorkspaceId);
|
|
83
|
+
if (!runtime) {
|
|
84
|
+
const sessionOwners = /* @__PURE__ */ new Map();
|
|
85
|
+
const core = createWorkspaceBridgeRuntimeCore({
|
|
86
|
+
handlers: options.workspaceBridge?.handlers,
|
|
87
|
+
ownerWorkspaceId: safeWorkspaceId
|
|
88
|
+
});
|
|
89
|
+
runtime = {
|
|
90
|
+
registry: core.registry,
|
|
91
|
+
idempotencyStore: new InMemoryWorkspaceBridgeIdempotencyStore(),
|
|
92
|
+
refreshTokenStore: new InMemoryWorkspaceBridgeRuntimeRefreshTokenStore(),
|
|
93
|
+
sessionOwners
|
|
94
|
+
};
|
|
95
|
+
runtimes.set(safeWorkspaceId, runtime);
|
|
96
|
+
}
|
|
97
|
+
return runtime;
|
|
98
|
+
};
|
|
99
|
+
const resolveBridgeWorkspaceId = async (request) => {
|
|
100
|
+
const authHeader = request.headers.authorization;
|
|
101
|
+
if (options.workspaceBridge?.runtimeTokenSecret && typeof authHeader === "string" && authHeader.startsWith("Bearer ")) {
|
|
102
|
+
return verifyWorkspaceBridgeRuntimeToken(authHeader.slice("Bearer ".length), {
|
|
103
|
+
secret: options.workspaceBridge.runtimeTokenSecret
|
|
104
|
+
}).authContext.workspaceId;
|
|
105
|
+
}
|
|
106
|
+
return await resolveWorkspaceId(request);
|
|
107
|
+
};
|
|
108
|
+
const rememberSessionOwner = async (request) => {
|
|
109
|
+
const user = request.user;
|
|
110
|
+
if (!user?.id) return;
|
|
111
|
+
const sessionId = agentSessionId(request);
|
|
112
|
+
if (!sessionId) return;
|
|
113
|
+
const workspaceId = await resolveWorkspaceId(request);
|
|
114
|
+
const runtime = getRuntime(workspaceId);
|
|
115
|
+
runtime.sessionOwners.delete(sessionId);
|
|
116
|
+
runtime.sessionOwners.set(sessionId, user.id);
|
|
117
|
+
while (runtime.sessionOwners.size > MAX_SESSION_OWNER_CACHE) {
|
|
118
|
+
const oldest = runtime.sessionOwners.keys().next().value;
|
|
119
|
+
if (oldest === void 0) break;
|
|
120
|
+
runtime.sessionOwners.delete(oldest);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const callAsRuntime = async (workspaceId, request, callOptions) => {
|
|
124
|
+
const runtime = getRuntime(workspaceId);
|
|
125
|
+
const definition = runtime.registry.getDefinition(request.op);
|
|
126
|
+
if (!definition) {
|
|
127
|
+
return await runtime.registry.call(request, {
|
|
128
|
+
callerClass: "runtime",
|
|
129
|
+
workspaceId,
|
|
130
|
+
sessionId: callOptions?.sessionId,
|
|
131
|
+
capabilities: [],
|
|
132
|
+
actor: {
|
|
133
|
+
actorKind: "agent",
|
|
134
|
+
performedBy: { label: "agent-runtime" },
|
|
135
|
+
onBehalfOf: callOptions?.sessionId ? { label: `session:${callOptions.sessionId}` } : { label: `workspace:${workspaceId}` }
|
|
136
|
+
},
|
|
137
|
+
signal: callOptions?.signal,
|
|
138
|
+
emitUiEffect: (cmd) => getBridge(workspaceId).emitUiEffect(cmd)
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const ownerPrincipalId = callOptions?.sessionId ? runtime.sessionOwners.get(callOptions.sessionId) : void 0;
|
|
142
|
+
const authContext = {
|
|
143
|
+
callerClass: "runtime",
|
|
144
|
+
workspaceId,
|
|
145
|
+
sessionId: callOptions?.sessionId,
|
|
146
|
+
capabilities: [...definition.requiredCapabilities],
|
|
147
|
+
actor: {
|
|
148
|
+
actorKind: "agent",
|
|
149
|
+
performedBy: { label: "agent-runtime" },
|
|
150
|
+
onBehalfOf: ownerPrincipalId ? { id: ownerPrincipalId, label: `user:${ownerPrincipalId}` } : callOptions?.sessionId ? { label: `session:${callOptions.sessionId}` } : { label: `workspace:${workspaceId}` }
|
|
151
|
+
},
|
|
152
|
+
signal: callOptions?.signal,
|
|
153
|
+
emitUiEffect: (cmd) => getBridge(workspaceId).emitUiEffect(cmd)
|
|
154
|
+
};
|
|
155
|
+
return await runWithWorkspaceBridgeIdempotency(runtime.idempotencyStore, {
|
|
156
|
+
definition,
|
|
157
|
+
request,
|
|
158
|
+
auth: authContext
|
|
159
|
+
}, async () => await runtime.registry.call(request, authContext));
|
|
160
|
+
};
|
|
161
|
+
const runtimeEnvContribution = options.workspaceBridge?.runtimeTokenSecret || options.workspaceBridge?.runtimeEnv ? {
|
|
162
|
+
id: "workspace-bridge-runtime-env",
|
|
163
|
+
getEnv: async (ctx) => {
|
|
164
|
+
const contribution = createWorkspaceBridgeRuntimeEnvContribution({
|
|
165
|
+
workspaceId: ctx.workspaceId,
|
|
166
|
+
runtimeMode: ctx.runtimeMode,
|
|
167
|
+
registry: getRuntime(ctx.workspaceId).registry,
|
|
168
|
+
runtimeTokenSecret: options.workspaceBridge?.runtimeTokenSecret,
|
|
169
|
+
runtimeRefreshTokenSecret: options.workspaceBridge?.runtimeRefreshTokenSecret,
|
|
170
|
+
runtimeEnv: options.workspaceBridge?.runtimeEnv
|
|
171
|
+
});
|
|
172
|
+
return contribution ? await contribution.getEnv(ctx) : {};
|
|
173
|
+
}
|
|
174
|
+
} : void 0;
|
|
175
|
+
const registerHttpRoutes = async (app) => {
|
|
176
|
+
await app.register(workspaceBridgeHttpRoutes, {
|
|
177
|
+
getRegistry: async (request) => getRuntime(await resolveBridgeWorkspaceId(request)).registry,
|
|
178
|
+
getIdempotencyStore: async (request) => getRuntime(await resolveBridgeWorkspaceId(request)).idempotencyStore,
|
|
179
|
+
runtimeTokenSecret: options.workspaceBridge?.runtimeTokenSecret,
|
|
180
|
+
runtimeRefreshTokenSecret: options.workspaceBridge?.runtimeRefreshTokenSecret,
|
|
181
|
+
getRuntimeRefreshTokenStore: (_request, claims) => getRuntime(claims.workspaceId).refreshTokenStore,
|
|
182
|
+
getOwnerWorkspaceId: async (request) => await resolveBridgeWorkspaceId(request),
|
|
183
|
+
browserAuthPolicy: createBrowserBridgeAuthPolicy({
|
|
184
|
+
getPrincipal: (input) => {
|
|
185
|
+
const user = input.request?.user;
|
|
186
|
+
return user?.id ? { userId: user.id, email: user.email ?? void 0 } : null;
|
|
187
|
+
},
|
|
188
|
+
authorizeWorkspace: async ({ principal, workspaceId, definition }) => ({
|
|
189
|
+
allowed: await workspaceStore.isMember(workspaceId, principal.userId),
|
|
190
|
+
capabilities: definition.requiredCapabilities
|
|
191
|
+
}),
|
|
192
|
+
allowedOrigins: options.corsOrigins,
|
|
193
|
+
requireCsrfHeader: true
|
|
194
|
+
})
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
return { getBridge, callAsRuntime, rememberSessionOwner, runtimeEnvContribution, registerHttpRoutes };
|
|
198
|
+
}
|
|
199
|
+
|
|
54
200
|
// src/server/telemetry/db.ts
|
|
55
201
|
var EVENT_NAME_PATTERN = /^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*){0,8}$/;
|
|
56
202
|
var SAFE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/;
|
|
@@ -223,6 +369,16 @@ var FRONTEND_AUTH_PAGES_SPA_ONLY = /* @__PURE__ */ new Set([
|
|
|
223
369
|
"/auth/callback/github",
|
|
224
370
|
"/auth/callback/google"
|
|
225
371
|
]);
|
|
372
|
+
function normalizeOptionalPath(value) {
|
|
373
|
+
const trimmed = value?.trim();
|
|
374
|
+
return trimmed ? trimmed : void 0;
|
|
375
|
+
}
|
|
376
|
+
function inferSessionRootForWorkspaceRoot(workspaceRoot, runtimeMode) {
|
|
377
|
+
if (runtimeMode !== "vercel-sandbox") return void 0;
|
|
378
|
+
const resolvedRoot = path.resolve(workspaceRoot);
|
|
379
|
+
if (path.basename(resolvedRoot) !== "workspaces") return void 0;
|
|
380
|
+
return path.join(path.dirname(resolvedRoot), "pi-sessions");
|
|
381
|
+
}
|
|
226
382
|
function resolveCoreLoadConfigOptions(options = {}, nodeEnv = process.env.NODE_ENV) {
|
|
227
383
|
return {
|
|
228
384
|
allowMissingSecrets: nodeEnv !== "production",
|
|
@@ -271,13 +427,14 @@ function mergePiOptions(base, override) {
|
|
|
271
427
|
function createUnavailableCorePluginBridge() {
|
|
272
428
|
const fail = () => {
|
|
273
429
|
throw new Error(
|
|
274
|
-
"Core static server plugins do not receive a workspace-scoped
|
|
430
|
+
"Core static server plugins do not receive a workspace-scoped WorkspaceBridge yet. Use request-scoped UI tools/routes in core, or createWorkspaceAgentServer for standalone plugin bridge support."
|
|
275
431
|
);
|
|
276
432
|
};
|
|
277
433
|
return {
|
|
278
434
|
getState: fail,
|
|
279
435
|
setState: fail,
|
|
280
436
|
postCommand: fail,
|
|
437
|
+
emitUiEffect: fail,
|
|
281
438
|
subscribeCommands: fail,
|
|
282
439
|
drainCommands: fail
|
|
283
440
|
};
|
|
@@ -313,6 +470,19 @@ async function pathIsFile(filePath) {
|
|
|
313
470
|
return false;
|
|
314
471
|
}
|
|
315
472
|
}
|
|
473
|
+
function agentSessionIdFromRequest(request) {
|
|
474
|
+
const url = request.url.split("?")[0] ?? request.url;
|
|
475
|
+
if (request.method === "POST" && url === "/api/v1/agent/chat") {
|
|
476
|
+
const body = request.body;
|
|
477
|
+
return typeof body?.sessionId === "string" && body.sessionId.trim() ? body.sessionId : void 0;
|
|
478
|
+
}
|
|
479
|
+
if (request.method === "POST" && url.endsWith("/followup") && url.startsWith("/api/v1/agent/chat/")) {
|
|
480
|
+
const params = request.params;
|
|
481
|
+
const sessionId = params?.sessionId ?? params?.id;
|
|
482
|
+
return typeof sessionId === "string" && sessionId.trim() ? sessionId : void 0;
|
|
483
|
+
}
|
|
484
|
+
return void 0;
|
|
485
|
+
}
|
|
316
486
|
function toHeaders(source) {
|
|
317
487
|
const headers = new Headers();
|
|
318
488
|
for (const [key, value] of Object.entries(source)) {
|
|
@@ -575,6 +745,8 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
575
745
|
const serveFrontend = options.serveFrontend ?? (process.env.NODE_ENV !== "development" && Boolean(appRoot));
|
|
576
746
|
const pluginWorkspaceRoot = process.cwd();
|
|
577
747
|
const workspaceRoot = options.workspaceRoot ?? process.env.BORING_AGENT_WORKSPACE_ROOT ?? process.cwd();
|
|
748
|
+
const agentRuntimeMode = options.runtimeModeAdapter?.id ?? options.mode ?? process.env.BORING_AGENT_MODE;
|
|
749
|
+
const sessionRoot = normalizeOptionalPath(options.sessionRoot) ?? normalizeOptionalPath(process.env.BORING_AGENT_SESSION_ROOT) ?? inferSessionRootForWorkspaceRoot(workspaceRoot, agentRuntimeMode);
|
|
578
750
|
registerTelemetryHooks(app, telemetry);
|
|
579
751
|
await registerCoreRoutes({ app, sql, db, userStore, workspaceStore });
|
|
580
752
|
if (serveFrontend && appRoot) {
|
|
@@ -591,30 +763,24 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
591
763
|
const defaultPackageRuntimePlugins = readWorkspacePluginPackageRuntimePlugins(defaultPluginPackagePaths);
|
|
592
764
|
const { systemPromptAppend: defaultPackageSystemPromptAppend, ...defaultPackagePiOptions } = defaultPackagePiSnapshot;
|
|
593
765
|
const staticSystemPromptAppend = [options.systemPromptAppend, defaultPackageSystemPromptAppend].filter(Boolean).join("\n\n") || void 0;
|
|
594
|
-
const defaultPluginDirEntries = defaultPluginPackagePaths.map((dir) => ({ dir, hotReload: false })).filter((entry) => hasDirServerPlugin(entry));
|
|
766
|
+
const defaultPluginDirEntries = defaultPluginPackagePaths.map((dir) => ({ dir, hotReload: false, trust: "internal" })).filter((entry) => hasDirServerPlugin(entry));
|
|
595
767
|
const pluginEntries = [
|
|
596
768
|
...defaultPluginDirEntries,
|
|
597
769
|
...options.plugins ?? []
|
|
598
770
|
];
|
|
599
|
-
const bridges = /* @__PURE__ */ new Map();
|
|
600
|
-
const getUiBridge = (workspaceId) => {
|
|
601
|
-
const safeWorkspaceId = validateWorkspaceIdSegment(workspaceId);
|
|
602
|
-
let bridge = bridges.get(safeWorkspaceId);
|
|
603
|
-
if (!bridge) {
|
|
604
|
-
bridge = createInMemoryBridge();
|
|
605
|
-
bridges.set(safeWorkspaceId, bridge);
|
|
606
|
-
}
|
|
607
|
-
return bridge;
|
|
608
|
-
};
|
|
609
771
|
const pluginResolveContext = {
|
|
610
772
|
workspaceRoot: pluginWorkspaceRoot,
|
|
611
773
|
bridge: createUnavailableCorePluginBridge()
|
|
612
774
|
};
|
|
613
775
|
const resolvedPlugins = await Promise.all(
|
|
614
|
-
pluginEntries.map((entry) =>
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
776
|
+
pluginEntries.map(async (entry) => {
|
|
777
|
+
const plugin = await resolveOnePluginEntry(
|
|
778
|
+
entry,
|
|
779
|
+
pluginResolveContext
|
|
780
|
+
);
|
|
781
|
+
assertWorkspaceBridgeHandlersTrusted(plugin, entry);
|
|
782
|
+
return plugin;
|
|
783
|
+
})
|
|
618
784
|
);
|
|
619
785
|
const externalPluginsEnabled = options.externalPlugins !== false;
|
|
620
786
|
const installPluginAuthoring = externalPluginsEnabled && options.installPluginAuthoring === true;
|
|
@@ -631,6 +797,23 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
631
797
|
const root = options.getWorkspaceRoot ? await options.getWorkspaceRoot(workspaceId, request) : await resolveWorkspaceRoot(workspaceRoot, workspaceId);
|
|
632
798
|
return root;
|
|
633
799
|
};
|
|
800
|
+
const coreBridge = createCoreWorkspaceBridge({
|
|
801
|
+
workspaceBridge: {
|
|
802
|
+
...options.workspaceBridge,
|
|
803
|
+
handlers: [
|
|
804
|
+
...options.workspaceBridge?.handlers ?? [],
|
|
805
|
+
...pluginCollection.workspaceBridgeHandlers ?? []
|
|
806
|
+
]
|
|
807
|
+
},
|
|
808
|
+
resolveWorkspaceId,
|
|
809
|
+
workspaceStore,
|
|
810
|
+
corsOrigins: app.config.cors.origins,
|
|
811
|
+
validateWorkspaceId: validateWorkspaceIdSegment,
|
|
812
|
+
agentSessionId: agentSessionIdFromRequest
|
|
813
|
+
});
|
|
814
|
+
app.addHook("preHandler", async (request) => {
|
|
815
|
+
await coreBridge.rememberSessionOwner(request);
|
|
816
|
+
});
|
|
634
817
|
const workerBaseUrl = process.env.BORING_WORKER_BASE_URL?.trim();
|
|
635
818
|
const remoteWorkerModeAdapter = workerBaseUrl ? createRemoteWorkerModeAdapter({ baseUrl: workerBaseUrl }) : void 0;
|
|
636
819
|
const piOptionsByRoot = /* @__PURE__ */ new Map();
|
|
@@ -655,8 +838,13 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
655
838
|
};
|
|
656
839
|
const resolvePiOptions = async (ctx) => {
|
|
657
840
|
const pluginOptions = remoteWorkerModeAdapter ? pluginCollection.agentOptions.pi : getPluginPiOptions(ctx.workspaceRoot);
|
|
841
|
+
const bridgePiOptions = options.getWorkspaceBridgePi ? await options.getWorkspaceBridgePi({
|
|
842
|
+
workspaceId: ctx.workspaceId,
|
|
843
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
844
|
+
callAsRuntime: async (request, callOptions) => await coreBridge.callAsRuntime(ctx.workspaceId, request, callOptions)
|
|
845
|
+
}) : void 0;
|
|
658
846
|
const callerOptions = options.getPi ? await options.getPi(ctx) : void 0;
|
|
659
|
-
return mergePiOptions(pluginOptions, callerOptions);
|
|
847
|
+
return mergePiOptions(mergePiOptions(pluginOptions, bridgePiOptions), callerOptions);
|
|
660
848
|
};
|
|
661
849
|
app.get("/api/v1/workspace/meta", async (request, reply) => {
|
|
662
850
|
try {
|
|
@@ -693,12 +881,19 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
693
881
|
systemPromptAppend: pluginCollection.agentOptions.systemPromptAppend,
|
|
694
882
|
pi: pluginCollection.agentOptions.pi,
|
|
695
883
|
getPi: resolvePiOptions,
|
|
884
|
+
sessionRoot,
|
|
696
885
|
getSessionNamespace: resolveSessionNamespace,
|
|
697
886
|
getExtraTools: async (ctx) => {
|
|
698
887
|
const callerTools = options.getExtraTools ? await options.getExtraTools(ctx) : [];
|
|
888
|
+
const bridgeTools = options.getWorkspaceBridgeExtraTools ? await options.getWorkspaceBridgeExtraTools({
|
|
889
|
+
workspaceId: ctx.workspaceId,
|
|
890
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
891
|
+
callAsRuntime: async (request, callOptions) => await coreBridge.callAsRuntime(ctx.workspaceId, request, callOptions)
|
|
892
|
+
}) : [];
|
|
699
893
|
return [
|
|
700
894
|
...callerTools,
|
|
701
|
-
...
|
|
895
|
+
...bridgeTools,
|
|
896
|
+
...createWorkspaceUiTools(coreBridge.getBridge(ctx.workspaceId), {
|
|
702
897
|
workspaceRoot: ctx.workspaceFsCapability === "strong" ? ctx.workspaceRoot : void 0
|
|
703
898
|
})
|
|
704
899
|
];
|
|
@@ -727,13 +922,18 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
727
922
|
provisionWorkspace: options.provisionWorkspace,
|
|
728
923
|
registerHealthRoute: options.registerHealthRoute ?? false,
|
|
729
924
|
telemetry,
|
|
730
|
-
metering: options.metering
|
|
925
|
+
metering: options.metering,
|
|
926
|
+
runtimeEnvContributions: [
|
|
927
|
+
...options.runtimeEnvContributions ?? [],
|
|
928
|
+
...coreBridge.runtimeEnvContribution ? [coreBridge.runtimeEnvContribution] : []
|
|
929
|
+
]
|
|
731
930
|
});
|
|
732
931
|
await app.register(uiRoutes, {
|
|
733
932
|
getWorkspaceId: resolveWorkspaceId,
|
|
734
|
-
getBridge: async (request) =>
|
|
933
|
+
getBridge: async (request) => coreBridge.getBridge(await resolveWorkspaceId(request)),
|
|
735
934
|
preserveStateKeys: pluginCollection.preservedUiStateKeys
|
|
736
935
|
});
|
|
936
|
+
await coreBridge.registerHttpRoutes(app);
|
|
737
937
|
for (const { routes } of pluginCollection.routeContributions) {
|
|
738
938
|
await app.register(routes);
|
|
739
939
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.56",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Foundation package for boring-ui-v2 apps: DB, auth, config, HTTP app factory, and frontend app shell.",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"fastify-plugin": "^5.1.0",
|
|
71
71
|
"isomorphic-dompurify": "^3.10.0",
|
|
72
72
|
"lucide-react": "^1.8.0",
|
|
73
|
-
"nodemailer": "^8.0.
|
|
73
|
+
"nodemailer": "^8.0.9",
|
|
74
74
|
"pino": "^10.3.1",
|
|
75
75
|
"postgres": "^3.4.9",
|
|
76
76
|
"posthog-node": "^5.35.1",
|
|
@@ -79,9 +79,9 @@
|
|
|
79
79
|
"react-router-dom": "^7.14.2",
|
|
80
80
|
"smol-toml": "^1.6.1",
|
|
81
81
|
"zod": "^3.25.76",
|
|
82
|
-
"@hachej/boring-
|
|
83
|
-
"@hachej/boring-
|
|
84
|
-
"@hachej/boring-
|
|
82
|
+
"@hachej/boring-agent": "0.1.56",
|
|
83
|
+
"@hachej/boring-ui-kit": "0.1.56",
|
|
84
|
+
"@hachej/boring-workspace": "0.1.56"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"@testing-library/jest-dom": "^6.9.1",
|