@hachej/boring-core 0.1.54 → 0.1.55
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/server/index.d.ts +23 -1
- package/dist/app/server/index.js +208 -21
- package/package.json +4 -4
|
@@ -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}$/;
|
|
@@ -271,13 +417,14 @@ function mergePiOptions(base, override) {
|
|
|
271
417
|
function createUnavailableCorePluginBridge() {
|
|
272
418
|
const fail = () => {
|
|
273
419
|
throw new Error(
|
|
274
|
-
"Core static server plugins do not receive a workspace-scoped
|
|
420
|
+
"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
421
|
);
|
|
276
422
|
};
|
|
277
423
|
return {
|
|
278
424
|
getState: fail,
|
|
279
425
|
setState: fail,
|
|
280
426
|
postCommand: fail,
|
|
427
|
+
emitUiEffect: fail,
|
|
281
428
|
subscribeCommands: fail,
|
|
282
429
|
drainCommands: fail
|
|
283
430
|
};
|
|
@@ -313,6 +460,19 @@ async function pathIsFile(filePath) {
|
|
|
313
460
|
return false;
|
|
314
461
|
}
|
|
315
462
|
}
|
|
463
|
+
function agentSessionIdFromRequest(request) {
|
|
464
|
+
const url = request.url.split("?")[0] ?? request.url;
|
|
465
|
+
if (request.method === "POST" && url === "/api/v1/agent/chat") {
|
|
466
|
+
const body = request.body;
|
|
467
|
+
return typeof body?.sessionId === "string" && body.sessionId.trim() ? body.sessionId : void 0;
|
|
468
|
+
}
|
|
469
|
+
if (request.method === "POST" && url.endsWith("/followup") && url.startsWith("/api/v1/agent/chat/")) {
|
|
470
|
+
const params = request.params;
|
|
471
|
+
const sessionId = params?.sessionId ?? params?.id;
|
|
472
|
+
return typeof sessionId === "string" && sessionId.trim() ? sessionId : void 0;
|
|
473
|
+
}
|
|
474
|
+
return void 0;
|
|
475
|
+
}
|
|
316
476
|
function toHeaders(source) {
|
|
317
477
|
const headers = new Headers();
|
|
318
478
|
for (const [key, value] of Object.entries(source)) {
|
|
@@ -591,30 +751,24 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
591
751
|
const defaultPackageRuntimePlugins = readWorkspacePluginPackageRuntimePlugins(defaultPluginPackagePaths);
|
|
592
752
|
const { systemPromptAppend: defaultPackageSystemPromptAppend, ...defaultPackagePiOptions } = defaultPackagePiSnapshot;
|
|
593
753
|
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));
|
|
754
|
+
const defaultPluginDirEntries = defaultPluginPackagePaths.map((dir) => ({ dir, hotReload: false, trust: "internal" })).filter((entry) => hasDirServerPlugin(entry));
|
|
595
755
|
const pluginEntries = [
|
|
596
756
|
...defaultPluginDirEntries,
|
|
597
757
|
...options.plugins ?? []
|
|
598
758
|
];
|
|
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
759
|
const pluginResolveContext = {
|
|
610
760
|
workspaceRoot: pluginWorkspaceRoot,
|
|
611
761
|
bridge: createUnavailableCorePluginBridge()
|
|
612
762
|
};
|
|
613
763
|
const resolvedPlugins = await Promise.all(
|
|
614
|
-
pluginEntries.map((entry) =>
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
764
|
+
pluginEntries.map(async (entry) => {
|
|
765
|
+
const plugin = await resolveOnePluginEntry(
|
|
766
|
+
entry,
|
|
767
|
+
pluginResolveContext
|
|
768
|
+
);
|
|
769
|
+
assertWorkspaceBridgeHandlersTrusted(plugin, entry);
|
|
770
|
+
return plugin;
|
|
771
|
+
})
|
|
618
772
|
);
|
|
619
773
|
const externalPluginsEnabled = options.externalPlugins !== false;
|
|
620
774
|
const installPluginAuthoring = externalPluginsEnabled && options.installPluginAuthoring === true;
|
|
@@ -631,6 +785,23 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
631
785
|
const root = options.getWorkspaceRoot ? await options.getWorkspaceRoot(workspaceId, request) : await resolveWorkspaceRoot(workspaceRoot, workspaceId);
|
|
632
786
|
return root;
|
|
633
787
|
};
|
|
788
|
+
const coreBridge = createCoreWorkspaceBridge({
|
|
789
|
+
workspaceBridge: {
|
|
790
|
+
...options.workspaceBridge,
|
|
791
|
+
handlers: [
|
|
792
|
+
...options.workspaceBridge?.handlers ?? [],
|
|
793
|
+
...pluginCollection.workspaceBridgeHandlers ?? []
|
|
794
|
+
]
|
|
795
|
+
},
|
|
796
|
+
resolveWorkspaceId,
|
|
797
|
+
workspaceStore,
|
|
798
|
+
corsOrigins: app.config.cors.origins,
|
|
799
|
+
validateWorkspaceId: validateWorkspaceIdSegment,
|
|
800
|
+
agentSessionId: agentSessionIdFromRequest
|
|
801
|
+
});
|
|
802
|
+
app.addHook("preHandler", async (request) => {
|
|
803
|
+
await coreBridge.rememberSessionOwner(request);
|
|
804
|
+
});
|
|
634
805
|
const workerBaseUrl = process.env.BORING_WORKER_BASE_URL?.trim();
|
|
635
806
|
const remoteWorkerModeAdapter = workerBaseUrl ? createRemoteWorkerModeAdapter({ baseUrl: workerBaseUrl }) : void 0;
|
|
636
807
|
const piOptionsByRoot = /* @__PURE__ */ new Map();
|
|
@@ -655,8 +826,13 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
655
826
|
};
|
|
656
827
|
const resolvePiOptions = async (ctx) => {
|
|
657
828
|
const pluginOptions = remoteWorkerModeAdapter ? pluginCollection.agentOptions.pi : getPluginPiOptions(ctx.workspaceRoot);
|
|
829
|
+
const bridgePiOptions = options.getWorkspaceBridgePi ? await options.getWorkspaceBridgePi({
|
|
830
|
+
workspaceId: ctx.workspaceId,
|
|
831
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
832
|
+
callAsRuntime: async (request, callOptions) => await coreBridge.callAsRuntime(ctx.workspaceId, request, callOptions)
|
|
833
|
+
}) : void 0;
|
|
658
834
|
const callerOptions = options.getPi ? await options.getPi(ctx) : void 0;
|
|
659
|
-
return mergePiOptions(pluginOptions, callerOptions);
|
|
835
|
+
return mergePiOptions(mergePiOptions(pluginOptions, bridgePiOptions), callerOptions);
|
|
660
836
|
};
|
|
661
837
|
app.get("/api/v1/workspace/meta", async (request, reply) => {
|
|
662
838
|
try {
|
|
@@ -696,9 +872,15 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
696
872
|
getSessionNamespace: resolveSessionNamespace,
|
|
697
873
|
getExtraTools: async (ctx) => {
|
|
698
874
|
const callerTools = options.getExtraTools ? await options.getExtraTools(ctx) : [];
|
|
875
|
+
const bridgeTools = options.getWorkspaceBridgeExtraTools ? await options.getWorkspaceBridgeExtraTools({
|
|
876
|
+
workspaceId: ctx.workspaceId,
|
|
877
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
878
|
+
callAsRuntime: async (request, callOptions) => await coreBridge.callAsRuntime(ctx.workspaceId, request, callOptions)
|
|
879
|
+
}) : [];
|
|
699
880
|
return [
|
|
700
881
|
...callerTools,
|
|
701
|
-
...
|
|
882
|
+
...bridgeTools,
|
|
883
|
+
...createWorkspaceUiTools(coreBridge.getBridge(ctx.workspaceId), {
|
|
702
884
|
workspaceRoot: ctx.workspaceFsCapability === "strong" ? ctx.workspaceRoot : void 0
|
|
703
885
|
})
|
|
704
886
|
];
|
|
@@ -727,13 +909,18 @@ async function createCoreWorkspaceAgentServer(options = {}) {
|
|
|
727
909
|
provisionWorkspace: options.provisionWorkspace,
|
|
728
910
|
registerHealthRoute: options.registerHealthRoute ?? false,
|
|
729
911
|
telemetry,
|
|
730
|
-
metering: options.metering
|
|
912
|
+
metering: options.metering,
|
|
913
|
+
runtimeEnvContributions: [
|
|
914
|
+
...options.runtimeEnvContributions ?? [],
|
|
915
|
+
...coreBridge.runtimeEnvContribution ? [coreBridge.runtimeEnvContribution] : []
|
|
916
|
+
]
|
|
731
917
|
});
|
|
732
918
|
await app.register(uiRoutes, {
|
|
733
919
|
getWorkspaceId: resolveWorkspaceId,
|
|
734
|
-
getBridge: async (request) =>
|
|
920
|
+
getBridge: async (request) => coreBridge.getBridge(await resolveWorkspaceId(request)),
|
|
735
921
|
preserveStateKeys: pluginCollection.preservedUiStateKeys
|
|
736
922
|
});
|
|
923
|
+
await coreBridge.registerHttpRoutes(app);
|
|
737
924
|
for (const { routes } of pluginCollection.routeContributions) {
|
|
738
925
|
await app.register(routes);
|
|
739
926
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.55",
|
|
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.",
|
|
@@ -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-workspace": "0.1.
|
|
84
|
-
"@hachej/boring-
|
|
82
|
+
"@hachej/boring-agent": "0.1.55",
|
|
83
|
+
"@hachej/boring-workspace": "0.1.55",
|
|
84
|
+
"@hachej/boring-ui-kit": "0.1.55"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"@testing-library/jest-dom": "^6.9.1",
|