@openspecui/server 3.6.1 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +172 -58
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { CliExecutor, CodeEditorThemeSchema, ConfigManager, CustomSoundHashSchema, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, GitConfigSchema, HOSTED_SHELL_PROTOCOL_VERSION, MarkdownParser, NotificationPublishInputSchema, NotificationSettingsSchema, OPENSPECUI_HOOKS_VERSION, OpenSpecAdapter, OpenSpecWatcher, OpsxConfigSchema, OpsxKernel, PtyClientMessageSchema, ReactiveContext, TerminalConfigSchema, TerminalControlParser, TerminalRendererEngineSchema, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getToolInitStates, getWatcherRuntimeStatus, initWatcherPool, isWatcherPoolInitialized, resolveTerminalShellDefaults, sniffGlobalCli, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput } from "@openspecui/core";
2
- import { basename, dirname, join, relative, resolve, sep } from "node:path";
1
+ import { CliExecutor, CodeEditorThemeSchema, ConfigManager, CustomSoundHashSchema, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, DocumentTranslationConfigSchema, GitConfigSchema, HOSTED_SHELL_PROTOCOL_VERSION, MarkdownParser, NotificationPublishInputSchema, NotificationSettingsSchema, OPENSPECUI_HOOKS_VERSION, OpenSpecAdapter, OpenSpecWatcher, OpsxConfigSchema, OpsxKernel, PtyClientMessageSchema, ReactiveContext, TerminalConfigSchema, TerminalControlParser, TerminalRendererEngineSchema, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getToolInitStates, getWatcherRuntimeStatus, initWatcherPool, isWatcherPoolInitialized, parseOpsxEntityMetadata, parseOpsxSchemaDetail, resolveTerminalShellDefaults, sniffGlobalCli, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput } from "@openspecui/core";
2
+ import { basename, dirname, join, matchesGlob, relative, resolve, sep } from "node:path";
3
3
  import { access, mkdir, readFile, realpath, rm, stat, writeFile } from "node:fs/promises";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
5
5
  import { createServer as createServer$1 } from "node:net";
@@ -35,6 +35,9 @@ function toErrorDiagnostic$1(error) {
35
35
  function isNotNull(value) {
36
36
  return value !== null;
37
37
  }
38
+ function normalizeChangeFilePath(path) {
39
+ return path.replace(/\\/g, "/").replace(/^\/+/, "");
40
+ }
38
41
  var DocumentService = class {
39
42
  parser = new MarkdownParser();
40
43
  constructor(projectDir, adapter, hookRuntime) {
@@ -176,37 +179,125 @@ var DocumentService = class {
176
179
  return null;
177
180
  }
178
181
  }
182
+ async readEntityDetail(stage, changeId, consumer = "view", mode = "processed", options = {}) {
183
+ const detail = await this.adapter.readEntityDetail(stage, changeId, options);
184
+ if (!detail) return null;
185
+ const root = stage === "change" ? `openspec/changes/${changeId}` : `openspec/changes/archive/${changeId}`;
186
+ const processedByPath = /* @__PURE__ */ new Map();
187
+ const processArtifactFile = async (artifact, file) => {
188
+ const artifactFile = {
189
+ ...await this.processEntityFile({
190
+ stage,
191
+ changeId,
192
+ root,
193
+ file,
194
+ consumer,
195
+ mode,
196
+ schemaName: detail.schemaName,
197
+ artifactId: artifact.id,
198
+ artifactOutputPath: artifact.outputPath
199
+ }),
200
+ type: "file"
201
+ };
202
+ processedByPath.set(file.path, artifactFile);
203
+ return artifactFile;
204
+ };
205
+ const artifacts = await Promise.all(detail.artifacts.map(async (artifact) => ({
206
+ ...artifact,
207
+ files: await Promise.all(artifact.files.map((file) => processArtifactFile(artifact, file)))
208
+ })));
209
+ const files = await Promise.all(detail.files.map(async (file) => {
210
+ const processed = processedByPath.get(file.path);
211
+ if (processed) return processed;
212
+ return this.processEntityFile({
213
+ stage,
214
+ changeId,
215
+ root,
216
+ file,
217
+ consumer,
218
+ mode,
219
+ schemaName: detail.schemaName
220
+ });
221
+ }));
222
+ const filesByPath = new Map(files.map((file) => [file.path, file]));
223
+ const ungroupedFiles = detail.ungroupedFiles.map((file) => filesByPath.get(file.path) ?? file);
224
+ return {
225
+ ...detail,
226
+ files,
227
+ artifacts,
228
+ ungroupedFiles
229
+ };
230
+ }
179
231
  async readChangeFiles(changeId, consumer = "view", mode = "processed") {
180
232
  const files = await this.adapter.readChangeFiles(changeId);
181
233
  return this.processChangeFiles("change", changeId, files, consumer, mode);
182
234
  }
235
+ async readChangeArtifactOutput(changeId, outputPath, consumer = "view", mode = "processed") {
236
+ const normalizedPath = normalizeChangeFilePath(outputPath);
237
+ return (await this.readChangeArtifactFiles(changeId, normalizedPath, consumer, mode)).find((file) => file.path === normalizedPath)?.content ?? null;
238
+ }
239
+ async readChangeGlobArtifactFiles(changeId, outputPath, consumer = "view", mode = "processed") {
240
+ const normalizedPattern = normalizeChangeFilePath(outputPath);
241
+ return this.readChangeArtifactFiles(changeId, normalizedPattern, consumer, mode);
242
+ }
183
243
  async readArchivedChangeFiles(changeId, consumer = "view", mode = "processed") {
184
244
  const files = await this.adapter.readArchivedChangeFiles(changeId);
185
245
  return this.processChangeFiles("archive", changeId, files, consumer, mode);
186
246
  }
187
247
  async processChangeFiles(stage, changeId, files, consumer, mode) {
188
248
  const root = stage === "change" ? `openspec/changes/${changeId}` : `openspec/changes/archive/${changeId}`;
189
- return (await Promise.all(files.map(async (file) => {
190
- if (file.type !== "file" || file.content === void 0 || !file.path.endsWith(".md")) return file;
191
- const kind = this.inferChangeFileKind(file.path);
192
- if (!kind) return file;
193
- const result = await this.processDocument({
194
- consumer,
195
- mode,
196
- document: {
197
- stage,
198
- kind,
199
- changeId,
200
- relativePath: `${root}/${file.path}`,
201
- absolutePath: join(this.projectDir, root, file.path)
202
- },
203
- source: file.content
204
- });
205
- return {
206
- ...file,
207
- content: result.markdown
208
- };
209
- }))).filter(isNotNull);
249
+ return (await Promise.all(files.map((file) => this.processChangeFile(stage, changeId, root, file, consumer, mode)))).filter(isNotNull);
250
+ }
251
+ async readChangeArtifactFiles(changeId, outputPath, consumer, mode) {
252
+ const matchingFiles = (await this.adapter.readChangeFiles(changeId)).filter((file) => {
253
+ if (file.type !== "file" || file.content === void 0) return false;
254
+ return matchesGlob(file.path, outputPath) || file.path === outputPath;
255
+ });
256
+ const root = `openspec/changes/${changeId}`;
257
+ return (await Promise.all(matchingFiles.map((file) => this.processChangeFile("change", changeId, root, file, consumer, mode)))).filter(isNotNull).filter((file) => file.type === "file" && file.content !== void 0);
258
+ }
259
+ async processChangeFile(stage, changeId, root, file, consumer, mode) {
260
+ if (file.type !== "file" || file.content === void 0 || !file.path.endsWith(".md")) return file;
261
+ const kind = this.inferChangeFileKind(file.path);
262
+ if (!kind) return file;
263
+ const result = await this.processDocument({
264
+ consumer,
265
+ mode,
266
+ document: {
267
+ stage,
268
+ kind,
269
+ changeId,
270
+ relativePath: `${root}/${file.path}`,
271
+ absolutePath: join(this.projectDir, root, file.path)
272
+ },
273
+ source: file.content
274
+ });
275
+ return {
276
+ ...file,
277
+ content: result.markdown
278
+ };
279
+ }
280
+ async processEntityFile(input) {
281
+ if (input.file.type !== "file" || input.file.content === void 0 || !input.file.path.endsWith(".md")) return input.file;
282
+ const result = await this.processDocument({
283
+ consumer: input.consumer,
284
+ mode: input.mode,
285
+ document: {
286
+ stage: input.stage,
287
+ kind: "artifact",
288
+ changeId: input.changeId,
289
+ schemaName: input.schemaName,
290
+ artifactId: input.artifactId,
291
+ artifactOutputPath: input.artifactOutputPath,
292
+ relativePath: `${input.root}/${input.file.path}`,
293
+ absolutePath: join(this.projectDir, input.root, input.file.path)
294
+ },
295
+ source: input.file.content
296
+ });
297
+ return {
298
+ ...input.file,
299
+ content: result.markdown
300
+ };
210
301
  }
211
302
  inferChangeFileKind(path) {
212
303
  if (path === "proposal.md") return "proposal";
@@ -1194,16 +1285,11 @@ async function loadDashboardOverview(ctx, reason = "dashboard-refresh") {
1194
1285
  updatedAt: changeMeta.updatedAt
1195
1286
  }));
1196
1287
  const activeChanges = selectRecentDashboardItems(allActiveChanges);
1197
- const archivedChanges = (await Promise.all(archiveMetas.map(async (meta) => {
1198
- const change = await ctx.adapter.readArchivedChange(meta.id);
1199
- if (!change) return null;
1200
- return {
1201
- id: meta.id,
1202
- createdAt: meta.createdAt,
1203
- updatedAt: meta.updatedAt,
1204
- tasksCompleted: change.tasks.filter((task) => task.completed).length
1205
- };
1206
- }))).filter((item) => item !== null);
1288
+ const archivedChanges = archiveMetas.map((meta) => ({
1289
+ id: meta.id,
1290
+ createdAt: meta.createdAt,
1291
+ updatedAt: meta.updatedAt
1292
+ }));
1207
1293
  const allSpecifications = (await Promise.all(specMetas.map(async (meta) => {
1208
1294
  const spec = await ctx.adapter.readSpec(meta.id);
1209
1295
  if (!spec) return null;
@@ -1218,7 +1304,7 @@ async function loadDashboardOverview(ctx, reason = "dashboard-refresh") {
1218
1304
  const requirements = allSpecifications.reduce((sum, spec) => sum + spec.requirements, 0);
1219
1305
  const tasksTotal = allActiveChanges.reduce((sum, change) => sum + change.progress.total, 0);
1220
1306
  const tasksCompleted = allActiveChanges.reduce((sum, change) => sum + change.progress.completed, 0);
1221
- const archivedTasksCompleted = archivedChanges.reduce((sum, change) => sum + change.tasksCompleted, 0);
1307
+ const archivedTasksCompleted = 0;
1222
1308
  const taskCompletionPercent = tasksTotal > 0 ? Math.round(tasksCompleted / tasksTotal * 100) : null;
1223
1309
  const inProgressChanges = allActiveChanges.filter((change) => change.progress.total > 0 && change.progress.completed < change.progress.total).length;
1224
1310
  const specificationTrendEvents = specMetas.flatMap((spec) => {
@@ -1232,7 +1318,7 @@ async function loadDashboardOverview(ctx, reason = "dashboard-refresh") {
1232
1318
  const ts = parseDatedIdTimestamp(archive.id) ?? resolveTrendTimestamp(archive.updatedAt, archive.createdAt);
1233
1319
  return ts === null ? [] : [{
1234
1320
  ts,
1235
- value: archive.tasksCompleted
1321
+ value: 1
1236
1322
  }];
1237
1323
  });
1238
1324
  const specMetaById = new Map(specMetas.map((meta) => [meta.id, meta]));
@@ -1339,6 +1425,29 @@ async function loadDashboardOverview(ctx, reason = "dashboard-refresh") {
1339
1425
  };
1340
1426
  }
1341
1427
 
1428
+ //#endregion
1429
+ //#region src/entity-read-options.ts
1430
+ async function readEntityMetadata(ctx, stage, id) {
1431
+ return (stage === "change" ? await ctx.adapter.readChangeFiles(id) : await ctx.adapter.readArchivedChangeFiles(id)).find((file) => file.type === "file" && file.path === ".openspec.yaml")?.content ?? null;
1432
+ }
1433
+ async function buildEntityReadOptions(ctx, stage, id) {
1434
+ const schemaName = parseOpsxEntityMetadata(await readEntityMetadata(ctx, stage, id)).schemaName;
1435
+ if (!schemaName) return {};
1436
+ try {
1437
+ await ctx.kernel.waitForWarmup();
1438
+ await ctx.kernel.ensureSchemaDetail(schemaName);
1439
+ await ctx.kernel.ensureSchemaYaml(schemaName);
1440
+ const schemaYaml = ctx.kernel.getSchemaYaml(schemaName);
1441
+ const diagnostics = schemaYaml ? parseOpsxSchemaDetail(schemaYaml, schemaName, { path: `schema:${schemaName}` }).diagnostics : [];
1442
+ return {
1443
+ schemas: { [schemaName]: ctx.kernel.getSchemaDetail(schemaName) },
1444
+ schemaDiagnostics: diagnostics.length > 0 ? { [schemaName]: diagnostics } : void 0
1445
+ };
1446
+ } catch {
1447
+ return { schemas: {} };
1448
+ }
1449
+ }
1450
+
1342
1451
  //#endregion
1343
1452
  //#region src/notification-service.ts
1344
1453
  var NotificationService = class {
@@ -3283,19 +3392,21 @@ const archiveRouter = router({
3283
3392
  return ctx.adapter.listArchivedChangesWithMeta();
3284
3393
  }),
3285
3394
  get: publicProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => {
3286
- return ctx.documentService.readArchivedChange(input.id);
3395
+ return ctx.documentService.readEntityDetail("archive", input.id, "view", "processed", await buildEntityReadOptions(ctx, "archive", input.id));
3287
3396
  }),
3288
3397
  getRaw: publicProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => {
3289
- return ctx.adapter.readArchivedChangeRaw(input.id);
3398
+ return ctx.documentService.readEntityDetail("archive", input.id, "view", "source", await buildEntityReadOptions(ctx, "archive", input.id));
3290
3399
  }),
3291
3400
  subscribe: publicProcedure.subscription(({ ctx }) => {
3292
3401
  return createReactiveSubscription(() => ctx.adapter.listArchivedChangesWithMeta());
3293
3402
  }),
3294
3403
  subscribeOne: publicProcedure.input(z.object({ id: z.string() })).subscription(({ ctx, input }) => {
3295
- return createReactiveSubscriptionWithInput((id) => ctx.documentService.readArchivedChange(id))(input.id);
3404
+ return createReactiveSubscriptionWithInput(async (id) => ctx.documentService.readEntityDetail("archive", id, "view", "processed", await buildEntityReadOptions(ctx, "archive", id)))(input.id);
3296
3405
  }),
3297
3406
  subscribeFiles: publicProcedure.input(z.object({ id: z.string() })).subscription(({ ctx, input }) => {
3298
- return createReactiveSubscriptionWithInput((id) => ctx.adapter.readArchivedChangeFiles(id))(input.id);
3407
+ return createReactiveSubscriptionWithInput(async (id) => {
3408
+ return (await ctx.documentService.readEntityDetail("archive", id, "view", "processed", await buildEntityReadOptions(ctx, "archive", id)))?.files ?? [];
3409
+ })(input.id);
3299
3410
  })
3300
3411
  });
3301
3412
  z.object({
@@ -3397,13 +3508,14 @@ const configRouter = router({
3397
3508
  terminal: TerminalConfigSchema.omit({ rendererEngine: true }).partial().extend({ rendererEngine: TerminalRendererEngineSchema.optional() }).optional(),
3398
3509
  dashboard: DashboardConfigSchema.partial().optional(),
3399
3510
  git: GitConfigSchema.partial().optional(),
3400
- notifications: NotificationSettingsSchema.partial().optional()
3511
+ notifications: NotificationSettingsSchema.partial().optional(),
3512
+ translation: DocumentTranslationConfigSchema.partial().optional()
3401
3513
  })).mutation(async ({ ctx, input }) => {
3402
3514
  const hasCliCommand = input.cli !== void 0 && Object.prototype.hasOwnProperty.call(input.cli, "command");
3403
3515
  const hasCliArgs = input.cli !== void 0 && Object.prototype.hasOwnProperty.call(input.cli, "args");
3404
3516
  if (hasCliCommand && !hasCliArgs) {
3405
3517
  await ctx.configManager.setCliCommand(input.cli?.command ?? "");
3406
- if (input.theme !== void 0 || input.codeEditor !== void 0 || input.appBaseUrl !== void 0 || input.opsx !== void 0 || input.terminal !== void 0 || input.dashboard !== void 0 || input.git !== void 0 || input.notifications !== void 0) await ctx.configManager.writeConfig({
3518
+ if (input.theme !== void 0 || input.codeEditor !== void 0 || input.appBaseUrl !== void 0 || input.opsx !== void 0 || input.terminal !== void 0 || input.dashboard !== void 0 || input.git !== void 0 || input.notifications !== void 0 || input.translation !== void 0) await ctx.configManager.writeConfig({
3407
3519
  theme: input.theme,
3408
3520
  codeEditor: input.codeEditor,
3409
3521
  appBaseUrl: input.appBaseUrl,
@@ -3411,7 +3523,8 @@ const configRouter = router({
3411
3523
  terminal: input.terminal,
3412
3524
  dashboard: input.dashboard,
3413
3525
  git: input.git,
3414
- notifications: input.notifications
3526
+ notifications: input.notifications,
3527
+ translation: input.translation
3415
3528
  });
3416
3529
  return { success: true };
3417
3530
  }
@@ -3820,7 +3933,7 @@ const opsxRouter = router({
3820
3933
  })).query(async ({ ctx, input }) => {
3821
3934
  await ctx.kernel.waitForWarmup();
3822
3935
  await ctx.kernel.ensureArtifactOutput(input.changeId, input.outputPath);
3823
- return ctx.kernel.getArtifactOutput(input.changeId, input.outputPath);
3936
+ return ctx.documentService.readChangeArtifactOutput(input.changeId, input.outputPath, "view", "processed");
3824
3937
  }),
3825
3938
  subscribeArtifactOutput: publicProcedure.input(z.object({
3826
3939
  changeId: z.string(),
@@ -3829,7 +3942,7 @@ const opsxRouter = router({
3829
3942
  return createReactiveSubscription(async () => {
3830
3943
  await ctx.kernel.waitForWarmup();
3831
3944
  await ctx.kernel.ensureArtifactOutput(input.changeId, input.outputPath);
3832
- return ctx.kernel.getArtifactOutput(input.changeId, input.outputPath);
3945
+ return ctx.documentService.readChangeArtifactOutput(input.changeId, input.outputPath, "view", "processed");
3833
3946
  });
3834
3947
  }),
3835
3948
  readGlobArtifactFiles: publicProcedure.input(z.object({
@@ -3838,7 +3951,7 @@ const opsxRouter = router({
3838
3951
  })).query(async ({ ctx, input }) => {
3839
3952
  await ctx.kernel.waitForWarmup();
3840
3953
  await ctx.kernel.ensureGlobArtifactFiles(input.changeId, input.outputPath);
3841
- return ctx.kernel.getGlobArtifactFiles(input.changeId, input.outputPath);
3954
+ return ctx.documentService.readChangeGlobArtifactFiles(input.changeId, input.outputPath, "view", "processed");
3842
3955
  }),
3843
3956
  subscribeGlobArtifactFiles: publicProcedure.input(z.object({
3844
3957
  changeId: z.string(),
@@ -3847,7 +3960,7 @@ const opsxRouter = router({
3847
3960
  return createReactiveSubscription(async () => {
3848
3961
  await ctx.kernel.waitForWarmup();
3849
3962
  await ctx.kernel.ensureGlobArtifactFiles(input.changeId, input.outputPath);
3850
- return ctx.kernel.getGlobArtifactFiles(input.changeId, input.outputPath);
3963
+ return ctx.documentService.readChangeGlobArtifactFiles(input.changeId, input.outputPath, "view", "processed");
3851
3964
  });
3852
3965
  }),
3853
3966
  writeArtifactOutput: publicProcedure.input(z.object({
@@ -4067,7 +4180,7 @@ const appRouter = router({
4067
4180
  function joinParts(parts) {
4068
4181
  return parts.map((part) => part?.trim() ?? "").filter((part) => part.length > 0).join("\n\n");
4069
4182
  }
4070
- async function collectSearchDocuments(adapter, documentService) {
4183
+ async function collectSearchDocuments(adapter, documentService, resolveEntityReadOptions) {
4071
4184
  const docs = [];
4072
4185
  const specs = await adapter.listSpecsWithMeta();
4073
4186
  for (const spec of specs) {
@@ -4104,20 +4217,16 @@ async function collectSearchDocuments(adapter, documentService) {
4104
4217
  }
4105
4218
  const archives = await adapter.listArchivedChangesWithMeta();
4106
4219
  for (const archive of archives) {
4107
- const raw = documentService ? await documentService.readArchivedChangeRaw(archive.id, "search", "processed") : await adapter.readArchivedChangeRaw(archive.id);
4108
- if (!raw) continue;
4220
+ const entityOptions = resolveEntityReadOptions ? await resolveEntityReadOptions("archive", archive.id) : void 0;
4221
+ const entity = documentService ? await documentService.readEntityDetail("archive", archive.id, "search", "processed", entityOptions) : await adapter.readEntityDetail("archive", archive.id);
4222
+ if (!entity) continue;
4109
4223
  docs.push({
4110
4224
  id: `archive:${archive.id}`,
4111
4225
  kind: "archive",
4112
4226
  title: archive.name,
4113
4227
  href: `/archive/${encodeURIComponent(archive.id)}`,
4114
4228
  path: `openspec/changes/archive/${archive.id}`,
4115
- content: joinParts([
4116
- typeof raw.proposal === "string" ? raw.proposal : raw.proposal.markdown,
4117
- typeof raw.tasks === "string" ? raw.tasks : raw.tasks.markdown,
4118
- typeof raw.design === "string" ? raw.design : raw.design?.markdown,
4119
- ...raw.deltaSpecs.map((deltaSpec) => deltaSpec.content)
4120
- ]),
4229
+ content: joinParts(entity.files.filter((file) => file.type === "file").map((file) => file.content)),
4121
4230
  updatedAt: archive.updatedAt
4122
4231
  });
4123
4232
  }
@@ -4133,9 +4242,10 @@ var SearchService = class {
4133
4242
  initPromise = null;
4134
4243
  rebuildPromise = null;
4135
4244
  rebuildTimer = null;
4136
- constructor(adapter, watcher, provider = new NodeWorkerSearchProvider(), documentService) {
4245
+ constructor(adapter, watcher, provider = new NodeWorkerSearchProvider(), documentService, resolveEntityReadOptions) {
4137
4246
  this.adapter = adapter;
4138
4247
  this.documentService = documentService;
4248
+ this.resolveEntityReadOptions = resolveEntityReadOptions;
4139
4249
  this.provider = provider;
4140
4250
  watcher?.on("change", () => {
4141
4251
  this.scheduleRebuild();
@@ -4181,7 +4291,7 @@ var SearchService = class {
4181
4291
  if (!forceInit && !this.initialized) return;
4182
4292
  if (this.rebuildPromise) return this.rebuildPromise;
4183
4293
  this.rebuildPromise = (async () => {
4184
- const docs = await collectSearchDocuments(this.adapter, this.documentService);
4294
+ const docs = await collectSearchDocuments(this.adapter, this.documentService, this.resolveEntityReadOptions);
4185
4295
  if (this.initialized) await this.provider.replaceAll(docs);
4186
4296
  else {
4187
4297
  await this.provider.init(docs);
@@ -4454,7 +4564,11 @@ function createServer(config) {
4454
4564
  const notificationService = new NotificationService();
4455
4565
  const customSoundService = new CustomSoundService();
4456
4566
  const watcher = config.enableWatcher !== false ? new OpenSpecWatcher(config.projectDir) : void 0;
4457
- const searchService = new SearchService(adapter, watcher, void 0, documentService);
4567
+ const entityReadOptionsContext = {
4568
+ adapter,
4569
+ kernel
4570
+ };
4571
+ const searchService = new SearchService(adapter, watcher, void 0, documentService, (stage, id) => buildEntityReadOptions(entityReadOptionsContext, stage, id));
4458
4572
  const dashboardOverviewService = new DashboardOverviewService((reason) => loadDashboardOverview({
4459
4573
  adapter,
4460
4574
  configManager,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openspecui/server",
3
- "version": "3.6.1",
3
+ "version": "3.7.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.mjs",
6
6
  "exports": {